> ⚠️ 严老师 Day 1 (2026-07-27) 在飞书实际推送为 0 条消息(可能是 p2…
0推送消息数
0字符数
2026-07-27教学日期
> ⚠️ 严老师 Day 1 (2026-07-27) 在飞书实际推送为 0 条消息(可能是 p2p chat 尚未建立,或当时推送到了别处)。以下为 MEMORY 留存的摘要便条,仅供索引。完整课堂原文未能恢复。
- **主题**:值类型 vs 引用类型(内存布局 / 装箱拆箱)
- **起点水平确认**:C# 资深(主人主动告知,2026-07-27 16:50)
- **深度策略**:跳过基础概念,直接开到底层(IL、JIT、GC、ref struct、Generic Math 等资深级内容)
- **推送渠道**:飞书 DM(`teacher-yan-bot`),分 5 段推送成功
### 2026-07-24(项目启动)
- 主题:_项目初始化,等待首次教学_
- 起点水平:_默认"中级偏入门"_ → **已更正为"资深"**
---
## 📚 已教知识点索引
> 用于去重。每天推送前**必查**,跳过已教内容。
### C# 语言基础
| # | 知识点 | 教学日 | 主人掌握度 | 备注 |
|---|--------|--------|------------|------|
| 1 | 值类型 vs 引用类型(内存布局 / 装箱拆箱 / 现代 .NET 演进) | 2026-07-27 | 待评估 | Day 1 内容,已推送 |
| 2 | 泛型的本质(运行时擦除 vs 编译期特化 / 泛型共享 / MakeGenericType) | 2026-07-28 | 待评估 | Day 2 内容,已推送 |
| 3 | 委托与事件(MulticastDelegate 内存布局 / 多播链 / 事件封装性 / 闭包 DisplayClass / CommandManager 泄漏) | 2026-08-01 | 待评估 | Day 3 内容,已推送 |
| 4 | async/await 状态机(`IAsyncStateMachine` / MoveNext / SynchronizationContext / `ValueTask` / `IValueTaskSource`) | 2026-08-03 | 待评估 | Day 4 内容,已推送 |
| 5 | LINQ 延迟执行与表达式树(`IEnumerable` vs `IQueryable` / `ExpressionVisitor` / EF Core SQL 翻译 / 强类型字段引用) | 2026-08-04 | 待评估 | Day 5 内容,已推送 |
| 6 | 反射与元数据(CLR Metadata 40+ 表 / RID-token / `MethodInfo.Invoke` 装箱 / `Delegate.CreateDelegate` 强类型委托 / `Expression.Compile` IL 生成 / `[DynamicallyAccessedMembers]` AOT 注解 / `MetadataLoadContext`) | 2026-08-05 | 待评估 | Day 6 内容,已推送 |
| 7 | Span / Memory(`ref struct` 零拷贝切片 / JIT 边界检查消除 / SIMD / `ArrayPool` 池化 / System.IO.Pipelines 5 层零分配架构 / WPF 高吞吐日志分析实战) | 2026-08-06 | 待评估 | Day 7 内容,已推送(**Phase 1 收官**) |
### .NET 运行时 / 底层
| # | 知识点 | 教学日 | 主人掌握度 | 备注 |
|---|--------|--------|------------|------|
| 8 | CLR 内存模型与 GC 机制(分代假设 / Gen0-1-2 / LOH 不压缩 / POH pinned 对象堆 / Workstation vs Server GC / Background GC / `GCSettings.LatencyMode` / `GC.TryStartNoGCRegion` / WPF 实时行情 5 层零 GC 架构) | 2026-08-07 | 待评估 | Day 8 内容,已推送(**Phase 2 开篇**) |
| 9 | JIT 编译与 RyuJIT(双层编译 / RyuJIT vs 旧 x86 JIT / Tiered Compilation 0/1/2 / Dynamic PGO / ReadyToRun (R2R) / ProfileOptimization 多核并行 / WPF 冷启动 5 层优化架构) | 2026-08-08 | 待评估 | Day 9 内容,已推送(**Phase 2 第 2 课**) |
| 10 | 异常处理机制(CIL Exception Handling Table / Windows SEH / 栈展开 / 零开销哲学 / `throw` vs `throw ex` / First/Second Chance Exception / ExceptionDispatchInfo / WPF 金融交易终端 5 层异常架构 / Try-Parse 模式 vs Result<T> vs OneOf) | 2026-08-09 | 待评估 | Day 10 内容,已推送(**Phase 2 第 3 课**) |
| 11 | CLR 线程池与 TaskScheduler(`ThreadPool` 4 大哲学 / Work-Stealing 本地 LIFO + 偷取 FIFO / Hill Climbing 爬山算法 / Starvation Avoidance 主动注入 ~500ms 阈值 / `TaskScheduler` 解耦 / `TaskCreationOptions.LongRunning` 独立线程 / WPF DispatcherSynchronizationContext / 真异步 I/O 走 IOCP 不占 worker thread / 5 层并发架构 / 6 大反模式) | 2026-08-10 | 待评估 | Day 11 内容,已推送(**Phase 2 第 4 课**) |
| 12 | 锁机制(`lock` / `Monitor` 语法糖 / `lockTaken` 异常安全 / `SemaphoreSlim` 异步信号量 / 自建 `AsyncLock` 生产模板 / `ReaderWriterLockSlim` 递归陷阱 / 对象头 Sync Block Index + Thin/Fat Lock 升级 / 内存屏障 acquire/release/full fence / `volatile` / `Monitor.Wait` 原子释放+阻塞 / `lock` 不能跨 await 3 大原因 / WPF 实时行情订阅 5 层架构) | 2026-08-11 | 待评估 | Day 12 内容,已推送(**Phase 2 第 5 课**) |
| 13 | .NET 并行编程(`Parallel.For/ForEach` / `PLINQ` / `Channel<T>` / `TPL Dataflow` / `Task.WhenAll` 5 大原语对比 / 静态 vs 动态 vs Chunk 分区 / 数据并行 vs 流水线并行 / `BoundedChannelFullMode` 5 种背压策略 / `BoundedCapacity` + `PropagateCompletion` / WPF 5000 张 4K 图像 5 层流水线实战 + 5 大方案对比 + 内存控制 4 策略 + 性能预期 8x 加速比) | 2026-08-12 | 待评估 | Day 13 内容,已推送(**Phase 2 第 6 课**) |
### WPF 核心
| # | 知识点 | 教学日 | 主人掌握度 | 备注 |
|---|--------|--------|------------|------|
| 14 | WPF 架构总览(7 层程序集 / `WindowsBase`↔`PresentationCore`↔`PresentationFramework` 依赖关系 / XAML 编译原理 BAML / `InitializeComponent` 反射加载机制 / `Milcore` C++ COM 跨进程 / WPF 不能 NativeAOT 5 大根因 / R2R + 插件化 + 自包含部署替代方案) | 2026-08-13 | 待评估 | Day 14 内容,已推送(**Phase 3 开篇**) |
| 15 | WPF 依赖属性(DP)核心(CLR 9 大局限 / `DependencyObject`+`EffectiveValueEntry[]` 懒分配 / 11 级值解析优先级 / Validate/Coerce/PropertyChanged 3 大回调执行顺序 / `RegisterReadOnly`+`DependencyPropertyKey` / `SetValue` vs `SetCurrentValue` / `OverrideMetadata` vs `AddOwner` / `RegisterAttached` 附加属性 / `PasswordStrengthIndicator` 完整生产级实战) | 2026-08-14 | 待评估 | Day 15 内容,已推送(**Phase 3 第 2 课**) |
| 16 | WPF 路由事件(Routed Event)核心(CLR 事件 vs 路由事件本质区别 / 3 种路由策略 Bubble/Tunnel/Direct / `RoutedEventArgs.Source` vs `OriginalSource` / 视觉树 vs 逻辑树 / `EventManager.RegisterRoutedEvent` + `RaiseEvent` 完整 3 步生产代码 / `Handled` 机制 + `handledEventsToo: true` 父容器精确拦截 / 附加事件 vs 路由事件 / `EditableUserCard` 双模式 ListBox 实战 + 5 大生产陷阱) | 2026-08-15 | 待评估 | Day 16 内容,已推送(**Phase 3 第 3 课**) |
| 17 | WPF 视觉树与逻辑树(两棵树正交分离 + `VisualTreeHelper` O(1) 字段查找 vs `LogicalTreeHelper.GetParent` O(深度) 递归 + `OnApplyTemplate` 唯一模板入口 + 主题切换重新实例化悬空引用 + `ConditionalWeakTable` FindAncestor 缓存 + `VisualBrush` 强制实时渲染双刃剑 + `InputHitTest` 命中测试 + `Ellipse.Tag` 绑业务数据 + `ChartCard` 数据可视化工具完整生产实战 + 5 大生产陷阱) | 2026-08-16 | 待评估 | Day 17 内容,已推送(**Phase 3 第 4 课**) |
| 18 | WPF 布局系统(Layout System)核心(Measure/Arrange 两遍机制 / 4 个核心类 UIElement/FrameworkElement/Panel/Visual / 11 步布局管线 / DesiredSize vs RenderSize 5 维对比 / InvalidateMeasure vs InvalidateArrange 6 维对比 / 6 大内置 Panel 策略表 / 6 大常见误区 / WrapPanel 完整 300 行生产代码含 Margin/Orientation/换行算法 + 5 大生产陷阱 T1-T5 + 3 个扩展思考 FlexPanel 性能/浮点阈值/DPI 缩放 + 关联知识网络 DP/路由事件/视觉树/动画/绑定/自定义控件/DrawingVisual) | 2026-08-17 | 待评估 | Day 18 内容,已推送(**Phase 3 第 5 课**) |
| 19 | WPF 资源系统核心(`ResourceDictionary` / `StaticResource` 值快照 / `DynamicResource` `ResourceReferenceExpression` 表达式订阅 / `MergedDictionaries` 单向合并后覆盖前 / 资源查找沿逻辑树向上深度优先 / `FindResource` vs `TryFindResource` 找不到行为差异 / 隐式样式 `Style TargetType="..."` 无 Key 自动应用 / ThemeService 动态皮肤切换生产代码 / 4 种方案对比 / 5 大生产陷阱 T1-T5 / 3 大性能优化 / 3 个动手扩展思考) | 2026-08-19 | 待评估 | Day 19 内容,已推送(**Phase 3 第 6 课**) |
| 20 | WPF 数据绑定深入(Binding 核心机制 / 4 大 BindingMode + Default 二义性 / 5 大 BindingSource 8 维对比表 / `DataContext` 沿逻辑树单向继承 + `ItemsControl` 模板突变机制 / `Path` 完整语法 / **`TemplateBinding` 编译期优化压倒性优势**根因 / `IValueConverter` 5 实现要点 / `IMultiValueConverter` MultiBinding 5 陷阱 / `ValidationRule` 4 坑点 + `INotifyDataErrorInfo` 是企业级正解 / `INotifyPropertyChanged` 生产级模板含 `EqualityComparer<T>.Default`+`[CallerMemberName]`+静态 `PropertyChangedEventArgs` 缓存+跨线程 `Dispatcher.Invoke`+NaN 永远不等陷阱 / UserEditor 注册表单完整生产实战含 6 属性 BindingMode 决策表 / 5 大生产陷阱完整修复 T1-T5 / 3 个动手扩展思考 WeakDataContextChangedListener/Freezable 转换器/TypeDescriptionProvider) | 2026-08-20 | 待评估 | Day 20 内容,已推送(**Phase 3 第 7 课**) |
| 21 | WPF 命令系统(`ICommand` 接口契约 / `ICommandSource` 触发源 / `CommandBinding` 处理器 / `RoutedCommand`+`RoutedUICommand` 路由引擎 / 5 大内置命令集合 `ApplicationCommands`/`NavigationCommands`/`MediaCommands`/`EditingCommands`/`ComponentCommands` / 命令路由沿视觉树冒泡找 `CommandBinding` / **`CommandManager.RequerySuggested` 进程级静态事件 6 大触发时机 + 内存泄漏根因完整强引用链图** / `RelayCommand` 完整生产级实现含不挂 `RequerySuggested` 切断静态泄漏链 / `AsyncRelayCommand` 含 `_isExecuting` 防重入 + `OperationCanceledException` 安静处理 + `Dispose` 取消 CTS / `UserEditViewModel` 完整生产实战含 `ObservableObject` 基类 `alsoNotifyCommands` 自动通知命令 / 5 大生产陷阱完整修复 T1-T5 / 7 大易错点 / 3 个动手扩展思考 `WeakRelayCommand`/`ICommand.Undo` 命令模式/Roslyn 模仿 CommunityToolkit `[RelayCommand]`) | 2026-08-21 | 待评估 | Day 21 内容,已推送(**Phase 3 第 8 课**) |
| 22 | WPF 样式与模板系统(Style 4 大核心属性 TargetType/BasedOn/Resources/Setters + 选择器层级 隐式 vs 显式 vs 局部 + 优先级铁律 + ControlTemplate 5 大机制 PART_* 命名约定 + TemplateBinding 编译期优化 10-50x 根因 + ContentPresenter 自动应用 + OverridesDefaultStyle + DataTemplate 5 大机制 DataType 自动选择 + DataTemplateSelector 运行时选择 + HierarchicalDataTemplate 树形递归 + ItemTemplate 列表项 + ItemsPanelTemplate 4 大实战场景 + 4 大触发器 Trigger/MultiTrigger/DataTrigger/MultiDataTrigger/EventTrigger 完整对比 + Style Trigger vs VisualStateManager VSM 6 维辨析 + ValidationIndicator 完整生产代码 PART_* + 4 状态 DataTrigger + RelativeSource Self + Tooltip 动态更新 + 5 大生产陷阱 T1-T5 + 7 大易错点 + 3 个动手扩展思考 RatingControl/StepIndicator/TagInput + 8 道自检题) | 2026-08-22 | 待评估 | Day 22 内容,已推送(Phase 3 第 9 课) |
| 23 | WPF 自定义控件开发(Custom Control vs UserControl 7 维对比 + 5 大核心价值可主题化/模板可替换/扩展性强/复用粒度最优/PART_* 统一约定 + 5 大核心概念 Themes/Generic.xaml + [assembly: ThemeInfo] 完整加载机制 + DefaultStyleKeyProperty.OverrideMetadata 自定义控件灵魂调用 + PART_* 命名约定设计师-开发者契约 + OnApplyTemplate 4 大铁律 base 优先/null 检查/GetTemplateChild 唯一入口/主题切换重新展开 + 自定义 DP 标准模板 AffectsRender+Coerce+PropertyChangedCallback+SetCurrentValue 不打断绑定 + 自定义路由事件标准模板 EventManager.RegisterRoutedEvent+Bubble+RoutedPropertyChangedEventArgs<bool> 带 OldValue/NewValue + OnRender 直接绘制 vs VisualHost+VisualCollection+DrawingVisual 6 维对比 + 控件继承链选择 4 维分析 + Themes/Generic.xaml 完整 ToggleSwitch 模板 PART_Rail+PART_Thumb+TemplateBinding IsChecked 编译期优化+TranslateTransform X 动画式 Trigger+IsEnabled Trigger Opacity + 5 大常见误区表 + 关联知识网络 Day 14-22 大串联 + 3 道递进面试题) | 2026-08-23 | 待评估 | Day 23 内容,已推送(Phase 4 第 4 课) |
| 24 | WPF 动画系统(3 大动画分类 *Animation/*UsingKeyFrames/*AnimationUsingPath + Timeline 9 大子属性 BeginTime/Duration/RepeatBehavior/AutoReverse/SpeedRatio/AccelerationRatio/DecelerationRatio/FillBehavior + Storyboard 容器 + 4 大 KeyFrame Linear/Discrete/Spline/Easing + 11 大缓动函数族 Back/Bounce/Circle/Cubic/Elastic/Exponential/Power/Quadratic/Quartic/Quintic/Sine + EasingMode 3 选 1 EaseIn/EaseOut/EaseInOut + 5 大触发方式 EventTrigger/DataTrigger/Style.Triggers/Storyboard.Begin/VSM + FillBehavior HoldEnd vs Stop 完整对比 + **11 级 DP 优先级中动画位置 第 2 高 ⭐ 仅次于 Coerce 覆盖 LocalValue** + LoadingIndicator 完整生产代码含 4 大动画模式 + Themes/Generic.xaml + 5 大生产陷阱 T1-T5 + 7 大易错点 + 3 个动手扩展思考 CompositionTarget.Rendering 逐帧/RenderCapability.Tier 硬件加速/WPF 动画引擎 TimelineManager ClockGroup 源码) | 2026-08-24 | 待评估 | Day 24 内容,已推送(Phase 4 第 5 课) |
| 25 | WPF UI 虚拟化(UI Virtualization)与大数据集合性能优化(4 大核心组件 VirtualizingPanel + IItemContainerGenerator + IScrollInfo + VirtualizationMode + 11 步完整工作流程 + Standard vs Recycling 3 维深度对比 + ScrollUnit Pixel vs Item + **完整 VariableHeightVirtualPanel 350 行生产代码**含 IScrollInfo 全部实现 + 与 DataGrid 内置虚拟化 7 维对比取舍 + 5 大生产陷阱 T1-T5 包裹 ScrollViewer 失效/ItemsPanel 写 StackPanel 失效/可变高度+ScrollUnit=Item 重排/Recycling 模式 INPC 缺失显示旧数据/虚拟化容器内 ElementName 引用失效 + 3 个动手扩展思考 数据虚拟化 + 自定义 VirtualizingWrapPanel + IL 层完整调用链 + 8 道自检题) | 2026-08-25 | 待评估 | Day 25 内容,已推送(Phase 4 第 6 课) |
| 26 | WPF 拖放(Drag & Drop)系统核心(5 层架构分层业务层/WPF API 层/DataObject 层/OLE 层/OS 层 + DoDragDrop 静态方法启动器 + DataObject 数据容器 + DragEventArgs 事件载荷三角色矩阵 + 5 大内置标准格式 UnicodeText/Text/FileDrop CF_HDROP/Bitmap/Serializable + 与 WinForms 拖放 5 维本质对比 + DataObject 跨进程序列化机制同进程引用/跨进程字节流序列化/延迟序列化 + 自定义 DataObject 格式 RegisterFormat 必须否则 GetDataPresent 永远 false + 接收 Windows Explorer 文件拖入 CF_HDROP 自动填充 string[] + **完整 FileExplorerControl 280 行生产代码**含 FileTransferPayload[Serializable]+CustomFormats 静态构造 RegisterFormat+源端 3 事件+目标端 4 事件+DragAdorner 自定义视觉装饰器 IsHitTestVisible=false 关键 + 5 大生产陷阱 T1-T5 BinaryFormatter .NET 5+ 抛 SerializationException/虚拟化容器拖动源被回收 Recycle 模式/DragOver 重操作卡死 10-30ms 触发一次/RegisterFormat 跨进程不识别/DragAdorner 默认拦截鼠标 → 8 道自检题) | 2026-08-26 | 待评估 | Day 26 内容,已推送(Phase 4 第 7 课) |
### MVVM / 架构模式
| # | 知识点 | 教学日 | 主人掌握度 | 备注 |
|---|--------|--------|------------|------|
| - | _空_ | - | - | - |
### 性能 / 调试 / 最佳实践
| # | 知识点 | 教学日 | 主人掌握度 | 备注 |
|---|--------|--------|------------|------|
| - | _空_ | - | - | - |
---
## 🎯 主人掌握度总评
> 综合评估主人整体水平。每日更新。
- **当前评估**:**C# 资深**(主人 2026-07-27 16:50 主动确认)
- **强项**:待 Day 1 反馈后评估
- **弱项**:待 Day 1 反馈后评估
- **教学节奏建议**:
- ✅ 跳过基础语法 / 简单概念
- ✅ 直接进入 IL / JIT / GC / 底层机制
- ✅ 引入 .NET 5+ 现代特性(`ref struct`、`Generic Math`、`record struct`)
- ✅ 面试题侧重"原理辨析"和"实战性能"
- ✅ 代码示例贴近高频/企业级场景
---
## 🛣️ 难度路径规划(草稿)
> 严老师自己维护的可教知识点队列。每次推完一个,弹下一个。
### Phase 1:C# 核心机制(4-6 周)
- [x] 值类型 vs 引用类型(内存布局 / 装箱拆箱) — **2026-07-27 Day 1 完成**
- [x] 泛型的本质(运行时擦除 vs 编译期特化) — **2026-07-28 Day 2 完成**
- [x] 委托与事件(本质 / 内存模型 / 闭包) — **2026-08-01 Day 3 完成**
- [x] async/await 状态机(IL 层 / SynchronizationContext / ValueTask / IValueTaskSource) — **2026-08-03 Day 4 完成**
- [x] LINQ 延迟执行与表达式树(`IEnumerable` vs `IQueryable` / `ExpressionVisitor` / EF Core 翻译) — **2026-08-04 Day 5 完成**
- [x] 反射与元数据(CLR Metadata / RID-token / Delegate.CreateDelegate / `[DynamicallyAccessedMembers]` / `MetadataLoadContext`) — **2026-08-05 Day 6 完成**
- [x] Span / Memory 与高性能编程(`ref struct` / JIT 边界检查消除 / `ArrayPool` / System.IO.Pipelines) — **2026-08-06 Day 7 完成** ✅ **Phase 1 收官**
### Phase 2:.NET 运行时(3-4 周)
- [x] CLR 内存模型(堆 / 栈 / GC 机制 / 代龄) — **2026-08-07 Day 8 完成**
- [x] JIT 编译与 RyuJIT — **2026-08-08 Day 9 完成**
- [x] 异常处理机制(栈展开 / SEH) — **2026-08-09 Day 10 完成**
- [x] 线程池与 TaskScheduler — **2026-08-10 Day 11 完成**
- [x] 锁机制(Monitor / SemaphoreSlim / AsyncLock) — **2026-08-11 Day 12 完成**
- [x] 并行编程(Parallel / PLINQ / Channels) — **2026-08-12 Day 13 完成** ✅ **Phase 2 收官**
### Phase 3:WPF 基础(4-6 周)
- [x] WPF 架构总览(PresentationFramework / PresentationCore / WindowsBase / System.Xaml / Milcore + XAML→BAML 编译 + InitializeComponent 反射 + 5 大不能 NativeAOT 根因) — **2026-08-13 Day 14 完成** ✅ **Phase 3 开篇**
- [x] 依赖属性(DependencyProperty)核心(CLR 9 大局限 + 11 级优先级 + 3 大回调 + `RegisterReadOnly`+`DependencyPropertyKey` + `SetCurrentValue` + `PasswordStrengthIndicator` 实战) — **2026-08-14 Day 15 完成**
- [x] 路由事件(Routed Event)(CLR 事件 vs 路由事件本质区别 + 3 种路由策略 Bubble/Tunnel/Direct + 完整传播顺序图 + Source vs OriginalSource 视觉树辨析 + Handled + handledEventsToo + 附加事件 + EditableUserCard 双模式 ListBox 实战 + 5 大生产陷阱) — **2026-08-15 Day 16 完成**
- [x] 视觉树与逻辑树(两棵树正交分离 + VisualTreeHelper vs LogicalTreeHelper + OnApplyTemplate 唯一入口 + 主题切换 + FindAncestor + VisualBrush + InputHitTest + ChartCard 实战 + 5 大陷阱) — **2026-08-16 Day 17 完成** ✅ **Phase 3 第 4 课**
- [x] 布局系统(Measure/Arrange 两遍机制 + 11 步管线 + DesiredSize vs RenderSize + InvalidateMeasure vs InvalidateArrange + 6 大内置 Panel 策略 + WrapPanel 完整生产代码 + 5 大生产陷阱 + 3 个扩展思考) — **2026-08-17 Day 18 完成** ✅ **Phase 3 第 5 课**
- [x] 资源系统(`ResourceDictionary` + `StaticResource` 值快照 vs `DynamicResource` 表达式订阅 + `MergedDictionaries` 单向合并后覆盖前 + 资源查找沿逻辑树向上深度优先 + 隐式样式 `Style TargetType="..."` + ThemeService 动态皮肤切换 + 5 大生产陷阱 + 3 大性能优化) — **2026-08-19 Day 19 完成** ✅ **Phase 3 第 6 课**
- [x] 数据绑定深入(Binding 核心机制 / 4 大 BindingMode + Default 二义性 / 5 大 BindingSource / `DataContext` 沿逻辑树继承 / **`TemplateBinding` 编译期优化** / `IValueConverter` 5 实现要点 / `IMultiValueConverter` MultiBinding / `ValidationRule` + `INotifyDataErrorInfo` / `INotifyPropertyChanged` 生产级模板 / UserEditor 注册表单完整生产实战 / 5 大生产陷阱 T1-T5 / 3 个动手扩展思考) — **2026-08-20 Day 20 完成** ✅ **Phase 3 第 7 课**
- [x] 命令系统(`ICommand` 接口契约 / `ICommandSource` 触发源 / `CommandBinding` 处理器 / `RoutedCommand`+`RoutedUICommand` 路由引擎 / 5 大内置命令集合 / **`CommandManager.RequerySuggested` 进程级静态事件内存泄漏根因完整强引用链图** / `RelayCommand` 完整生产级实现含切断静态泄漏链 / `AsyncRelayCommand` 含防重入+取消+异常隔离 / `UserEditViewModel` 完整生产实战 / 5 大生产陷阱 T1-T5 / 7 大易错点 / 3 个动手扩展思考) — **2026-08-21 Day 21 完成(Phase 3 第 8 课)**
- [x] 样式与模板(Style 4 大核心 / ControlTemplate 5 大机制 / DataTemplate 5 大机制 / ItemsPanelTemplate / TemplateBinding 编译期优化 / 4 大触发器 / ValidationIndicator 生产实战) — **2026-08-22 Day 22 完成 ✅**
### Phase 4:WPF 进阶(4-6 周)
- [x] 数据绑定深入(Binding 核心机制 / 4 大 BindingMode / 5 大 BindingSource / TemplateBinding 编译期优化 / IValueConverter / ValidationRule / INotifyPropertyChanged 生产级模板 / UserEditor 完整生产实战) — **2026-08-20 Day 20 完成 ✅**
- [x] 命令系统(ICommand / ICommandSource / CommandBinding / RoutedCommand / 5 大内置命令集合 / CommandManager.RequerySuggested 内存泄漏根因 / RelayCommand / AsyncRelayCommand / UserEditViewModel 完整实战) — **2026-08-21 Day 21 完成 ✅**
- [x] 样式与模板(Style / ControlTemplate / DataTemplate / ItemsPanelTemplate) — **2026-08-22 Day 22 完成 ✅**
- [x] 自定义控件开发(Custom Control vs UserControl 7 维对比 + 5 大核心价值 + 5 大核心概念 Themes/Generic.xaml + [assembly: ThemeInfo] 加载机制 + DefaultStyleKeyProperty.OverrideMetadata + PART_* 命名约定 + OnApplyTemplate 唯一入口 + 主题切换悬空引用 + 自定义 DP 标准模板 AffectsRender + Coerce + PropertyChangedCallback + 自定义路由事件标准模板 EventManager.RegisterRoutedEvent + RoutedPropertyChangedEventArgs + OnRender vs VisualHost+VisualCollection+DrawingVisual 性能对比 + 控件继承链选择 4 维分析 + Themes/Generic.xaml 完整 ToggleSwitch 模板 + 5 大常见误区表 + 3 道递进面试题) — **2026-08-23 Day 23 完成(Phase 4 第 4 课)✅**
- [x] 动画系统(Storyboard / 缓动函数 / Animation Timeline / 关键帧动画 / 路径动画 / 动画性能优化)— **2026-08-24 Day 24 完成(Phase 4 第 5 课)✅**
- [x] UI 虚拟化(UI Virtualization)+ 大数据集合性能优化(VirtualizingPanel + IItemContainerGenerator + IScrollInfo + VirtualizationMode Standard vs Recycling + ScrollUnit Pixel vs Item + 11 步完整工作流程 + VariableHeightVirtualPanel 350 行完整生产代码 + DataGrid 7 维对比取舍 + 5 大生产陷阱 T1-T5 + 3 个动手扩展思考 数据虚拟化 + VirtualizingWrapPanel + IL 层完整调用链 + 8 道自检题)— **2026-08-25 Day 25 完成(Phase 4 第 6 课)✅**
- [ ] 渲染优化(DrawingVisual / VisualHost / RenderTargetBitmap / DrawingContext)— Phase 4 第 7 课候选
- [ ] Dispatcher 与线程亲和性(DispatcherObject / DispatcherPriority / BeginInvoke / InvokeAsync / 跨线程访问 / Invoke 死锁)— Phase 4 第 8 课候选
- [ ] 触摸笔迹(Touch / Stylus / InkCanvas / 手写识别 / 多点触控)— Phase 4 第 9 课候选
- [ ] 性能分析与诊断(PerfView / ETW / dotnet-trace / WPF 性能计数器 / 渲染线程 / GPU 加速)— Phase 4 第 10 课候选
- [ ] 国际化与本地化(.resx + x:Static + DynamicResource + XAML 资源切换)— Phase 4 第 11 课候选
- [ ] Phase 4 收官实战(综合应用所有 Phase 3+4 知识的大型项目)— Phase 4 收官
### Phase 5:MVVM 架构(3-4 周)
- [ ] INotifyPropertyChanged 正确实现
- [ ] ViewModel 基类设计
- [ ] 命令模式(RelayCommand / AsyncCommand)
- [ ] 消息聚合器(Messenger / EventAggregator)
- [ ] 依赖注入容器选择
- [ ] 模块化与导航
### Phase 6:企业级实战(持续)
- [ ] Prism 框架
- [ ] Caliburn.Micro
- [ ] ReactiveUI / Rx.NET
- [ ] 单元测试(Moq / FluentAssertions)
- [ ] 性能分析与优化(dotTrace / PerfView)
- [ ] 调试技巧(IntelliTrace / dumps)
---
## 💬 主人常问问题
> 记录主人反复提到的疑问,下次主动解答。
- _空_
---
## 🚧 主人的卡点
> 主人反复卡住的点。下次出现时**优先**拆解。
- _空_
---
## 📝 待办(教学相关)
- [x] 首次会话:确认主人 C#/WPF 起点水平 — **已确认:资深**(2026-07-27)
- [ ] 首次会话:确认周末 / 补救策略(待主人告知)
- [x] 首次教学:从 Phase 1 第一个未教知识点开始 — **2026-07-27 完成 Day 1**
- [x] **下次起点(已完成)**:Phase 1 第 5 个 — LINQ 延迟执行与表达式树 ✅ **2026-08-04 Day 5 推送完成**
- [x] **下次起点(已完成)**:Phase 1 第 6 个 — 反射与元数据 ✅ **2026-08-05 Day 6 推送完成**
- [x] **下次起点(已完成)**:Phase 1 第 7 个 — Span\<T\> / Memory\<T\> 与高性能编程 ✅ **2026-08-06 Day 7 推送完成(Phase 1 收官)**
- [x] **下次起点(已完成)**:Phase 2 第 1 个 — CLR 内存模型与 GC 机制 ✅ **2026-08-07 Day 8 推送完成(Phase 2 开篇)**
- [x] **下次起点(已完成)**:Phase 2 第 2 个 — JIT 编译与 RyuJIT ✅ **2026-08-08 Day 9 推送完成**
- [x] **下次起点(已完成)**:Phase 2 第 3 个 — 异常处理机制(栈展开 / SEH)✅ **2026-08-09 Day 10 推送完成**
- [x] **下次起点(已完成)**:Phase 2 第 4 个 — CLR 线程池与 TaskScheduler ✅ **2026-08-10 Day 11 推送完成**
- [x] **下次起点(已完成)**:Phase 2 第 5 个 — 锁机制(`Monitor` / `SemaphoreSlim` / `AsyncLock` / 锁的内存模型语义 / 锁与 `CancellationToken` 集成 / `ReaderWriterLockSlim` 使用陷阱)✅ **2026-08-11 Day 12 推送完成**
- [x] **下次起点(已完成)**:Phase 2 第 6 个 — 并行编程(`Parallel` / PLINQ / `Channel<T>` / TPL Dataflow / `TransformBlock` / `ActionBlock` / 背压流水线 / WPF 多线程图像处理实战)✅ **2026-08-12 Day 13 推送完成(Phase 2 收官)**
- [x] **下次起点(已完成)**:Phase 3 第 1 个 — WPF 架构总览 ✅ **2026-08-13 Day 14 推送完成(Phase 3 开篇)**
- [x] **下次起点(已完成)**:Phase 3 第 2 个 — 依赖属性(DependencyProperty)核心 ✅ **2026-08-14 Day 15 推送完成**
- [x] **下次起点(已完成)**:Phase 3 第 3 个 — 路由事件(Routed Event)(CLR 事件 vs 路由事件本质区别 / 3 种路由策略 Bubble/Tunnel/Direct 执行顺序 / `RoutedEventArgs.Source` vs `OriginalSource` 区别 / `EventManager.RegisterRoutedEvent` + `RaiseEvent` / 附加事件 vs 路由事件 / 5 大使用陷阱)✅ **2026-08-15 Day 16 推送完成**
- [x] **下次起点(已完成)**:Phase 3 第 4 个 — 视觉树与逻辑树 ✅ **2026-08-16 Day 17 推送完成**
- [x] **下次起点(已完成)**:Phase 3 第 5 个 — 布局系统(Measure / Arrange 两遍机制 / `MeasureOverride` + `ArrangeOverride` / `Panel` 派生 / `DesiredSize` vs `RenderSize` / `InvalidateMeasure` vs `InvalidateArrange` / WrapPanel 完整生产代码 / 5 大陷阱)✅ **2026-08-17 Day 18 推送完成**
- [x] **下次起点(已完成)**:Phase 3 第 6 个 — 资源系统(`ResourceDictionary` + `StaticResource` 值快照 vs `DynamicResource` 表达式订阅 + `MergedDictionaries` 单向合并后覆盖前 + 资源查找沿逻辑树向上深度优先 + 隐式样式 `Style TargetType="..."` + ThemeService 动态皮肤切换 + 5 大生产陷阱 + 3 大性能优化)✅ **2026-08-19 Day 19 推送完成(Phase 3 第 6 课)**
- [x] **下次起点(已完成)**:Phase 3 第 7 个 — 数据绑定深入(Binding 核心机制 + 4 大 BindingMode + 5 大 BindingSource + TemplateBinding 编译期优化 + IValueConverter/IMultiValueConverter + ValidationRule + INotifyPropertyChanged 生产级模板 + UserEditor 完整生产实战 + 5 大生产陷阱)✅ **2026-08-20 Day 20 推送完成(Phase 3 第 7 课)**
- [x] **下次起点(已完成)**:Phase 3 第 8 个 — 命令系统(`ICommand` 接口契约 / `ICommandSource` / `CommandBinding` / `RoutedCommand` / 5 大内置命令集合 / **`CommandManager.RequerySuggested` 内存泄漏根因完整强引用链图** / `RelayCommand` 生产级实现 / `AsyncRelayCommand` 含防重入+取消+异常隔离 / `UserEditViewModel` 完整实战 / 5 大生产陷阱 / 7 大易错点 / 3 个动手扩展思考) ✅ **2026-08-21 Day 21 推送完成(Phase 3 第 8 课)**
- [x] **下次起点(已完成)**:Phase 3 第 10 个 — 自定义控件开发(Custom Control vs UserControl 7 维对比 + 5 大核心价值 + 5 大核心概念 Themes/Generic.xaml + [assembly: ThemeInfo] 加载机制 + DefaultStyleKeyProperty.OverrideMetadata + PART_* 命名约定 + OnApplyTemplate 唯一入口 + 主题切换悬空引用 + 自定义 DP 标准模板 AffectsRender + Coerce + PropertyChangedCallback + 自定义路由事件标准模板 EventManager.RegisterRoutedEvent + RoutedPropertyChangedEventArgs + OnRender 直接绘制 vs VisualHost+VisualCollection+DrawingVisual 性能对比 + 控件继承链选择 4 维分析 + Themes/Generic.xaml 完整 ToggleSwitch 模板 PART_Rail + PART_Thumb + TemplateBinding IsChecked 编译期优化 + TranslateTransform X 动画式 Trigger 开关切换 + 5 大常见误区表 + 关联知识网络 Day 14-22 大串联 + 3 道递进面试题) ✅ **2026-08-23 Day 23 推送完成(Phase 4 第 4 课)**
- [x] **下次起点(已完成)**:Phase 4 第 5 个 — 动画系统(3 大分类 *Animation/*UsingKeyFrames/*AnimationUsingPath + Timeline 9 大子属性 + Storyboard 容器 + 4 大 KeyFrame Linear/Discrete/Spline/Easing + 11 大缓动函数族 + EasingMode 3 选 1 + 5 大触发方式 + FillBehavior HoldEnd vs Stop + **11 级 DP 优先级中动画位置 第 2 高** + 完整 LoadingIndicator 生产代码含 4 大动画模式 + 5 大生产陷阱 + 3 个动手扩展思考)✅ **2026-08-24 Day 24 推送完成(Phase 4 第 5 课)**
- [x] **下次起点(已完成)**:Phase 4 第 6 个 — UI 虚拟化(UI Virtualization)+ 大数据集合性能优化(VirtualizingPanel + IItemContainerGenerator + IScrollInfo + VirtualizationMode Standard vs Recycling + ScrollUnit Pixel vs Item + 11 步完整工作流程 + VariableHeightVirtualPanel 350 行完整生产代码 + DataGrid 7 维对比取舍 + 5 大生产陷阱 T1-T5 + 3 个动手扩展思考 数据虚拟化 + VirtualizingWrapPanel + IL 层完整调用链 + 8 道自检题)✅ **2026-08-25 Day 25 推送完成(Phase 4 第 6 课)**
- [ ] **下次起点(当前)**:Phase 4 第 7 课候选 — **渲染优化(DrawingVisual / VisualHost / RenderTargetBitmap / DrawingContext / RenderCapability.Tier / 合成器线程 / BitmapCache)** —— 直接承接今日 UI 虚拟化讲解 100 万行流畅后"为什么这么快"的渲染层根因
---
## 📊 Day 23 教学记录(详细)
### 今日主题
WPF 自定义控件开发(Custom Control)核心(Custom Control vs UserControl 7 维对比 + 5 大核心价值 + 5 大核心概念 Themes/Generic.xaml/[assembly: ThemeInfo]/DefaultStyleKeyProperty.OverrideMetadata/PART_*/OnApplyTemplate + 自定义 DP 标准模板 + 自定义路由事件标准模板 + OnRender vs VisualHost+D中 DrawingVisual 性能对比 + 控件继承链选择 4 维分析 + Themes/Generic.xaml 完整 ToggleSwitch 模板 + 5 大常见误区表 + 关联知识网络 Day 14-22 大串联)
### 教学重点
- **核心**:自定义控件是 WPF 进阶的"金字塔尖"——综合运用 Day 14-22 学的所有知识点(架构、DP、路由事件、视觉树、资源、样式与模板、绑定、命令)
- **本质区别(7 维对比)**:
- 继承链:UserControl(具体类)/ CustomControl(Control 等通用基类)
- 代码位置:内嵌 XAML + 强耦合 / Themes/Generic.xaml 独立
- 主题化:❌ 不能 / ✅ 天然支持(Light/Dark/HighContrast)
- 复用粒度:实例级 / 类型级
- 来扩展:复制粘贴 / 派生 + 添加新 DP/路由事件
- 典型场景:业务组件组合 / UI 原语可换肤
- 模板位置:UserControl.xaml 内嵌 / Themes/Generic.xaml 独立字典
- **一句话本质**:用户控件 = "组合"(Composition,强类型 XAML 黑盒)vs 自定义控件 = "派生"(Inheritance,可主题化可换肤可扩展的 UI 原语)
- **关键洞察**:WPF 所有内置控件(Button/TextBox/ListBox/DataGrid)**全部**是"自定义控件"——没有内置 UserControl,那是 WPF 团队为了便利业务代码组合提供的便利类
- **5 大核心价值**:
1. **可主题化**(Themability)—— Themes/Generic.xaml 字典替换,控件代码零修改
2. **模板可替换**(Template Replaceable)—— 同一 ToggleSwitch 可圆形/方块/滑动,行为不变外观千变万化
3. **扩展性极强**(Extensibility)—— 派生新控件只需添加新 DP + 新 RoutedEvent + 新 CommandBinding
4. **复用粒度最优**(Reusable Across Apps)—— 写一次 100 个项目引用
5. **统一 PART_* 约定**(Designer-Developer Collaboration)—— 设计师改模板 XAML 时他知道 PART_Thumb/PART_Rail 不能改名,设计师和开发者并行工作互不阻塞
- **5 大核心概念**:
- **Themes/Generic.xaml + [assembly: ThemeInfo]** —— 资源字典文件位置 + resourceDictionaryLocation/resourceDictionarySourceName 两个参数;不写也行(默认找 Themes/Generic.xaml),但显式声明 2 大好处 IDE 感知 + AOT 追踪
- **DefaultStyleKeyProperty.OverrideMetadata** —— 自定义控件的"灵魂调用",不写它 Generic.xaml 永远找不到你的控件;每个 Control 派生类必须 Override 一份,否则都拿父类 Control 的样式
- **PART_* 命名约定 + OnApplyTemplate 4 大铁律** —— base() 优先 + null 检查清理旧订阅 + GetTemplateChild 唯一有效时机 + 主题切换再次调用
- **自定义 DP 标准模板** —— AffectsRender 标志 + CoerceValueCallback 防御性 + PropertyChangedCallback 触发视觉更新 + SetCurrentValue 不打断绑定的关键
- **自定义路由事件标准模板** —— EventManager.RegisterRoutedEvent(Bubble) + RoutedPropertyChangedEventArgs<T> 带 OldValue/NewValue + 业务事件和 UI 事件分离(CheckedChanged + Click)
- **OnRender vs VisualHost + VisualCollection + DrawingVisual 6 维对比**:使用门槛/命中测试/鼠标事件/重绘成本/性能/适用场景;选择铁律——简单控件 OnRender / 频繁更新 VisualHost;永远不要混用
- **Themes/Generic.xaml 完整 ToggleSwitch 模板设计 3 要点**:PART_Rail + PART_Thumb 严格命名契约 / TemplateBinding IsChecked 编译期优化(比 {Binding RelativeSource=TemplatedParent} 快 10-50 倍)/ TranslateTransform X 触发 Trigger 动画式切换
- **控件继承链选择 4 维分析**:FrameworkElement(纯绘制无焦点)/ Control(标准可点击)/ HeaderedItemsControl(带 Header 的树)/ ItemsControl + Selector(列表可选择);铁律——继承链尽量浅
- **5 大常见误区表**:忘 DefaultStyleKeyProperty.OverrideMetadata 显示空白方块 / UserControl.xaml 写 XAML 失去主题化 / OnApplyTemplate 外 GetTemplateChild 返回 null / PART_* 名字拼写不一致 / 自定义 DP 设 AffectsRender 没 PropertyChangedCallback
- **关联知识网络(Day 14-22 大串联)**:
- Day 14 WPF 架构 → [assembly: ThemeInfo] + Milcore 加载 ResourceDictionary
- Day 15 DP → 自定义 DP 注册 + Coerce + AffectsRender 标志
- Day 16 路由事件 → EventManager.RegisterRoutedEvent + RaiseEvent + RoutedPropertyChangedEventArgs
- Day 17 视觉树 → OnApplyTemplate 唯一 GetTemplateChild 入口 + 主题切换重新展开
- Day 18 布局 → FrameworkElement.MeasureOverride / ArrangeOverride(非默认布局需要重写)
- Day 19 资源系统 → Generic.xaml 主题字典 + MergedDictionaries(多个主题合并)
- Day 20 数据绑定 → TemplateBinding 编译期优化(ControlTemplate 内用)
- Day 21 命令系统 → 自定义 ICommand + CommandBinding(ToggleSwitch 暴露 ToggleCommand)
- Day 22 样式与模板 → TargetType={x:Type local:ToggleSwitch} 应用本控件样式 + PART_* 命名约定 + DataTrigger/Trigger
- **3 道递进面试题**:
- Q1(基础概念):7 维对比表完整 + 一句话本质 + Button/TextBox/ListBox 都是 CustomControl 洞察 + 3 个具体场景
- Q2(原理与辨析):Themes/Generic.xaml 7 步加载流程 + DefaultStyleKey 作用 + PART_* 4 大作用 + 主题切换 5 步完整流程 + 不清旧订阅内存泄漏根因(闭包 + 累积 N 个)
- Q3(实战与深度):完整 ToggleSwitch 生产代码 + Themes/Generic.xaml + OnRender vs DrawingVisual 选择 + 5 大常见误区完整错误示例与修复
- **周日特殊处理**:今天是周日(2026-08-23),主人可能在休息,已经告知"主人回答完三个题后再发完整复盘讲解,如果要先看复盘告诉我"
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
动画系统(Storyboard / 缓动函数 / Animation Timeline / 关键帧动画 / 路径动画 / 动画性能优化 / WPF 实时数据可视化动画实战)—— Day 24
---
## 📊 Day 21 教学记录(详细)
### 今日主题
WPF 命令系统(Command System)核心(`ICommand` / `ICommandSource` / `CommandBinding` / `RoutedCommand` / `RelayCommand` / `AsyncRelayCommand` / `CommandManager.RequerySuggested` 内存泄漏根因 / MVVM `UserEditViewModel` 完整生产实战)
### 教学重点
- **核心**:命令系统是连接 UI 操作和业务逻辑的桥梁——4 大核心抽象(`ICommand` 契约 / `ICommandSource` 触发源 / `CommandBinding` 处理器 / `RoutedCommand` 路由引擎)+ 4 大价值(单一入口/状态统一/输入手势统一/MVVM 解耦)
- **本质区别**:命令系统 ≠ 路由事件高级版;事件=通知(fire-and-forget),命令=请求-响应(CanExecute 反复被问 + Execute 一次性触发)
- **5 大内置命令集合**:`ApplicationCommands`(剪贴板/保存/打开)/ `NavigationCommands`(浏览/前进后退)/ `MediaCommands`(播放控制)/ `EditingCommands`(文本编辑)/ `ComponentCommands`(滚动/移动焦点),共 100+ 标准命令
- **`CommandManager.RequerySuggested` 内存泄漏根因完整强引用链**(与 Day 3 委托事件泄漏一脉相承):
```
GC Root: static CommandManager
↓ 强引用
RequerySuggested: MulticastDelegate
↓ _invocationList
CanExecuteChanged 委托
↓ Target
RelayCommand 实例
↓ _execute 字段
Action lambda → 闭包 DisplayClass → ViewModel 实例 → 整个 View 子树
```
关闭 Window 后 ViewModel 还活着 → N 次打开关闭累积 N 个 ViewModel
- **4 种解决方案对比**(A 手动通知切断静态引用链 ⭐推荐 / B WeakEventManager 子类复杂 / C WeakReference 包 CommandManager 不可行 / D CommunityToolkit 源生成器新项目首选)
- **`RelayCommand` 完整生产级代码**:
- 不挂 `CommandManager.RequerySuggested`(切断静态引用链)
- 维护自己的 `CanExecuteChanged` 事件 + `RaiseCanExecuteChanged()` 方法
- 无参重载(`Action` / `Func<bool>`)和带参重载(`Action<object?>` / `Predicate<object?>`)
- **`AsyncRelayCommand` 三铁律**:
- **防重入**:`_isExecuting` 标志位 + `CanExecute` 返回 false
- **异常隔离**:`try/catch (OperationCanceledException)` 安静 + `catch (Exception)` 兜底
- **资源释放**:`Dispose` 取消 `CancellationTokenSource`
- **`ObservableObject` 基类支持 `alsoNotifyCommands`**:属性变化时连带触发命令刷新(不挂全局的关键补丁)
- **Q3 完整 `UserEditViewModel` 生产代码**:Name/Age/Status/IsBusy 4 属性 + SaveCommand 同步含 canExecute / LoadCommand 异步含 IsBusy 联动 / XAML `UpdateSourceTrigger=PropertyChanged` 让 TextBox 每次输入触发 `Name` 变化 → 自动联动 SaveCommand
- **5 大生产陷阱**:T1 RaiseCanExecuteChanged 写在 await 后 UI 不立即禁用 / T2 异步命令忘 catch OperationCanceledException 进程崩溃 / T3 窗口关闭未取消异步命令 ObjectDisposedException / T4 CanExecute 内 int.Parse 抛异常 WPF 静默吞 / T5 RelayCommand 挂 RequerySuggested 经典内存泄漏
- **7 大易错点**:RequerySuggested 忘 Dispose 泄漏 / ViewModel IDisposable 未 Dispose / async void 未捕获异常 / CanExecute 耗时操作 / CommandParameter 虚拟化离屏 null / 同步 RelayCommand 内 Task.Wait 死锁 / ApplicationCommands 未注册静默失败
- **3 个动手扩展思考**:WeakRelayCommand(WeakReference 包 ViewModel)/ ICommand.Undo 命令模式(Ctrl+Z 撤销)/ Roslyn Analyzers 模仿 CommunityToolkit [RelayCommand]
- **关联知识网络**:
- Day 14 WPF 架构:CommandManager 在 PresentationFramework.dll
- Day 15 DP:ICommandSource.Command/CommandParameter/CommandTarget 都是 DP
- Day 16 路由事件:ExecutedRoutedEventArgs 是路由事件成员
- Day 17 两棵树:**RoutedCommand 沿视觉树冒泡找 CommandBinding**(不是逻辑树)
- Day 20 数据绑定:{Binding SaveCommand} 把 ViewModel.ICommand 绑到 Button.Command
- 纵向连接:命令系统是 Day 16 路由事件的"业务语义升级版"——事件告诉"发生了什么",命令告诉"用户想要做什么 + 现在能做吗 + 谁能做"
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
样式与模板(Style / ControlTemplate / DataTemplate / ItemsPanelTemplate / TemplateBinding / 4 大触发器)—— Day 22
---
## 📊 Day 4 教学记录(详细)
### 今日主题
async/await 状态机(编译器生成的 `IAsyncStateMachine` / `MoveNext` / `SynchronizationContext` 调度)
### 教学重点
- **核心**:async/await 是纯编译器语法糖——async 方法被重写为实现 `IAsyncStateMachine` 的 struct,await 拆分成 switch-case 分支 + 续作注册
- **状态机结构**:`<>1__state`(状态编号:-2=完成 / -1=初始 pending / 0~N=各 await 续作入口)、`<>t__builder`(AsyncTaskMethodBuilder 桥)、awaiter 字段、捕获变量字段
- **关键差异**:
- `AwaitUnsafeOnCompleted` vs `AwaitOnCompleted`(前者避免类型检查,更快)
- `SynchronizationContext` 跨平台抽象 vs `TaskScheduler` Task 专用
- `ConfigureAwait(false)` 的 IL 本质:只是 `continueOnCapturedContext` bool 参数
- **进阶武器**:`ValueTask<T>`(同步完成零分配)/`IValueTaskSource<T>`(池化 Task 源)/`Channel<T>`(背压解耦)/`CancellationToken`(协作式取消)
- **Q3 实战 WPF 实时数据 5 层架构**:
- 网络层:`IValueTaskSource<byte>` 池化 + `ArrayPool<byte>` 缓冲
- 接收层:`Channel<T>` + `ConfigureAwait(false)` + 后台线程循环
- 业务层:解析 + 节流(60fps 限速)
- 调度层:`Dispatcher.BeginInvoke` + `CompositionTarget.Rendering` 帧对齐
- UI 层:`ObservableCollection` + `VirtualizingStackPanel.Recycling`
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
LINQ 延迟执行与表达式树 —— Day 5
---
## 📊 Day 1 教学记录(详细)
### 今日主题
值类型 vs 引用类型(内存布局 / 装箱拆箱)
### 教学重点
- 内存布局本质:栈 / 堆 / 内联 / 引用语义
- IL 层 `box` / `unbox` / `unbox.any` 指令剖析
- 现代 .NET 演进:泛型特化、`Span<T>`、`record struct`、`Generic Math`、`in` / `readonly struct`
- Q3 实战:高频 tick 系统中的装箱点排查与 3 种优化方案(`List<T>` / `Span<T>+ArrayPool` / `INumber<T>`)
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
泛型的本质(运行时擦除 vs 编译期特化)—— Day 2 (✅ 2026-07-28 完成)
---
## 📊 Day 2 教学记录(详细)
### 今日主题
泛型的本质(运行时擦除 vs 编译期特化)
### 教学重点
- **核心对比**:.NET 编译期特化 + 运行时独立类型 vs Java 编译期类型擦除
- **运行时机制**:
- 开放泛型类型定义(`List<>`)vs 封闭泛型类型(`List<int>`)的 MethodTable 差异
- 泛型共享(Generic Sharing):引用类型共享 JIT 代码、值类型独立 JIT
- 泛型约束的 CLR 支持范围(`class` / `struct` / `new()` / 基类 / 接口 vs 枚举/运算符约束)
- **关键 API**:`typeof`、`MakeGenericType` / `MakeGenericMethod`、`default(T)`、协变 / 逆变
- **Q3 实战**:WPF 高性能事件总线
- 原 `Dictionary<Type, List<object>>` 实现的 4 大性能问题(装箱 / 虚调用 / 缓存局部性 / 锁竞争)
- 重构为分层类型特化:`static class EventBus<T> { static List<Action<T>> _subscribers; }`
- 测量:典型 5-10 倍性能提升
- 两大局限:静态字段内存泄漏 + Dictionary 缓存局部性差
- 改进方案:`WeakReference<T>` + `FrozenDictionary`(.NET 8+)
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
委托与事件(本质 / 内存模型 / 闭包)—— Day 3
---
## 📊 Day 3 教学记录(详细)
### 今日主题
委托与事件(本质 / 内存模型 / 闭包)
### 教学重点
- **委托本质**:编译器生成 `MulticastDelegate` 派生密封类,含 `_target` / `_methodPtr` / `_invocationList` 三字段
- **多播链**:`Delegate.Combine(a, b)` 创建**新**委托实例,`_invocationList` 是 `Delegate[]`;`a += b` 不修改 `a`
- **事件封装性**:`event` 是私有委托字段 + 编译器生成 `add`/`remove` 访问器(`Interlocked.CompareExchange` 线程安全);外部只能 `+=`/`-=`,不能 `=` 整体赋值或 `null` 清空
- **闭包陷阱**:所有被捕获局部变量提升到嵌套类 `<>c__DisplayClass` 的实例字段,每次方法调用都新建 DisplayClass + Action 实例(隐藏分配)
- **闭包循环陷阱**:循环中 `for (int i = 0; i < 3; i++) actions.Add(() => i)` 输出 `3 3 3`,需每次迭代拷贝变量
- **Action\<T\> 等价性**:`Action<int>` 与自定义 `delegate void Foo(int x)` 在 IL 层完全相同,只是避免类型膨胀 + 协变逆变预定义
- **Q3 实战 RelayCommand 泄漏**:
- **根因**:`CommandManager.RequerySuggested` 是进程级静态事件,`Delegate.Combine` 累积形成强引用链:静态 CommandManager → 委托链 → RelayCommand → Action → ViewModel
- **检测**:dump 对比 / Finalizer 探针 / `GC.GetTotalMemory` 前后差值
- **方案 1(手动通知)**:`add { }` / `remove { }` 留空切断静态事件链,ViewModel 显式调 `RaiseCanExecuteChanged()`
- **方案 2(WeakEventManager)**:自己写 `WeakEventManager` 子类,内部用 `WeakReference` 存储订阅者
- **方案 3(.NET 5+)**:`WeakReference<T>` + `ConditionalWeakTable` 自维护映射表
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
async/await 状态机 —— Day 4
---
## 📊 Day 5 教学记录(详细)
### 今日主题
LINQ 延迟执行与表达式树(`IEnumerable<T>` vs `IQueryable<T>` / `ExpressionVisitor` / EF Core SQL 翻译)
### 教学重点
- **核心**:LINQ 是两层抽象的协同——`IEnumerable<T>` + 迭代器(CPU) vs `IQueryable<T>` + 表达式树(Provider 翻译)
- **延迟执行本质**:链式调用只构造查询描述符(包装迭代器),`MoveNext()` 才执行;终结符(`ToList` / `Count` / `First`)触发立即执行
- **`Func` vs `Expression`**:
- `Func` 是指向 IL 方法的函数指针(黑盒)
- `Expression<Func>` 是数据结构(`Expression` 节点树),可遍历、可修改、可翻译
- **EF Core 翻译 5 步**:`IQueryable.Where` → `IQueryProvider` → `QueryCompiler` → `ExpressionVisitor` 深度遍历 → `RelationalSqlTranslatingExpressionVisitor` 生成 SQL + 参数化
- **常见误区**:
- `Where` 不立即执行
- `Func` ↔ `Expression` 不可直接互转(`Func → Expression` 需 `Compile()`)
- `AsEnumerable()` vs `AsQueryable()` 完全不同
- 多次枚举 `IEnumerable` 重跑管道、`IQueryable` 可能 N+1 查询
- **Q3 实战 WPF MVVM 动态过滤**:
- **架构**:UI 规则 → `FilterBuilder<T>.Build` → `Expression<Func<T, bool>>` → EF Core → SQL
- **4 层代码**:FilterRule 数据模型 + 强类型字段引用(`typeof(T).GetProperty` 反射校验)+ WPF ViewModel 集成 + 自定义 Visitor 扩展
- **5 种方案对比**:动态 LINQ / 手写 SQL 拼接 / DynamicExpresso / Lambda 拼接 / ExpressionVisitor 自建
- **最终选择**:ExpressionVisitor + 强类型字段引用(类型安全、可翻译、可扩展、可测试、性能最优)
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
反射与元数据 —— Day 6
---
## 📊 Day 6 教学记录(详细)
### 今日主题
反射与元数据(CLR Metadata + Reflection API + 现代替代方案)
### 教学重点
- **核心**:元数据是数据(PE 文件里的二进制表),反射是 API(遍历表的访问器)—— 二者是"数据"和"访问器"的关系
- **CLR 元数据本质**:PE 文件 `.text` 段内的 40+ 张表(`TypeDef` / `MethodDef` / `FieldDef` / `Param` / `Property` / `Event` / `AssemblyRef` / `TypeRef`),表之间通过 **RID(Record ID)** 或 **token(4 字节 = 3 字节表索引 + 1 字节表标识)** 互相引用
- **IL 与元数据关系**:IL 中每条指令操作数(如 `callvirt 0A000003`)的 token 解析依赖元数据表
- **3 层反射性能**:
- `MethodInfo.Invoke`:~1000-2000 ns(值类型装箱 + `object[]` 分配 + 反射虚调用)
- `Delegate.CreateDelegate`:~15 ns(生成强类型委托,JIT 内联展开,**零装箱**)
- `Expression.Compile`:~20 ns(生成 IL 动态方法,首次 ~50-100 μs)
- **`[DynamicallyAccessedMembers]` 根本问题**:AOT 剪裁器**无法通过反射追踪可达性**——`Activator.CreateInstance(typeof(T))` 会让剪裁器把整个 `T` 删掉,运行时 `MissingMethodException`。注解本质是**把"运行时反射需求"前移到编译期**,Roslyn 写 PE 元数据 + IL Trimmer 读元数据保留成员
- **`MetadataLoadContext` 轻量扫描**:不把程序集加载到主 ALC,避免 `Assembly.LoadFrom` 强引用泄漏,适合插件系统/依赖分析工具
- **5 个常见误区**:`Invoke` 装箱值类型 / 不缓存 `MemberInfo` / `Assembly.LoadFrom` 强引用泄漏 / 反射破坏 AOT 剪裁 / `BindingFlags` 漏写 internal/static/protected
- **关联知识**:依赖属性 `DependencyPropertyDescriptor` 元数据表 / `CommunityToolkit.Mvvm.SourceGenerators` 编译期生成 `INotifyPropertyChanged` / .NET 8 `PublishAot=true` + `[RequiresDynamicCode]` / `Expression.Compile` 内部也走反射+IL 生成
- **Q3 实战 WPF 通用属性映射器**:
- **3 种方案对比**:纯反射版 ~50 万次/s(同名匹配硬编码) / 表达式树+规则 ~30-50 万次/s(`Expression.Property` 链构造嵌套属性) / 源生成 ~1000 万次/s(`[Mapper]` 特性 + Roslyn 生成强类型代码)
- **嵌套属性处理**:split `"Address.Street"` → `["Address", "Street"]`,逐层构造 `Expression.Property` 链
- **行业现状**:AutoMapper 逐渐式微 / Mapster(表达式树) / Mapperly(源生成,性能比 AutoMapper 快 5-10 倍,零反射)
- **5 个易错点**:每次 Map 都重新编译(必须 `ConcurrentDictionary` 缓存)/ 表达式树线程安全(`Compile()` 本身线程安全,编译结果共享)/ 嵌套属性 null 检查(生成代码插入 `Address != null ? ... : null`)/ AOT 剪裁掉 `typeof(T)` 需 `[DynamicallyAccessedMembers]` / 值类型装箱(`TSource` struct 时破坏性能,源生成可避免)
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
Span\<T\> / Memory\<T\> 与高性能编程 —— Day 7
---
## 📊 Day 7 教学记录(详细)
### 今日主题
Span / Memory 与高性能编程(`ref struct` 零拷贝切片 / JIT 边界检查消除 / System.IO.Pipelines 5 层零分配架构)
### 教学重点
- **核心**:Span 是栈上值类型(`ref struct`),包装 `ref T _reference` + `int _length`,零拷贝切片、零分配
- **`ref struct` 4 大约束**:不能装箱 / 不能跨 await / 不能当类型参数 / 不能做字段(避免逃逸堆)
- **JIT 边界检查消除**:`ref T` 让 JIT 通过 `_reference + index` 单指令访问,循环里所有 `Length` 检查被消除 → 向量化 + SIMD 自动
- **Memory\<T\>**:Span 的"非 ref struct"版本(可跨 await / 装箱 / 异步),但有 `Span` 属性间接寻址(~1 ns 开销)
- **ArrayPool\<byte\>**:.NET Core 2.1+ 内置,池化复用数组,避免反复分配 Gen0
- **System.IO.Pipelines**:`Pipe` + `PipeReader` + `ReadOnlySequence<byte>`,5 层零分配(Socket → PipeWriter → 写入 → 解析 → UI)
- **Q3 实战**:WPF 实时日志分析 5 层零分配架构(网络 ArrayPool + Pipeline 背压 + Span 零拷贝解析 + Channel 背压 + Dispatcher 节流)
- **.NET 9 改进**:`allows ref struct` 泛型约束 + `ReadOnlySpan\<T\> Sequence<T>.AsSpan()` + 异步流零拷贝
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
CLR 内存模型与 GC 机制 —— Day 8
---
## 📊 Day 8 教学记录(详细)
### 今日主题
CLR 内存模型与 GC 机制(分代假设 / Gen0-1-2 / LOH / POH / Workstation vs Server GC / Background GC)
### 教学重点
- **分代假设**:Weak Generational Hypothesis 实证观察(绝大多数对象生命周期短 + 老对象引用新对象极少)→ 分代 GC 设计的根本依据
- **5 个堆区对比表**:
- Gen0(~256 KB,新生对象,频繁回收)
- Gen1(Gen0 与 Gen2 之间的过渡,桥接)
- Gen2(长命对象,偶尔回收,Full GC 才动)
- LOH(Large Object Heap,≥ 85,000 字节阈值由来:避免压缩成本 + LOH 数组多为长命缓冲)
- POH(Pinned Object Heap,.NET 5+,钉子对象独立堆,避免 Gen2 碎片)
- **关键 API**:`GC.GetGeneration(obj)` / `GC.Collect(gen, mode)` / `GC.TryStartNoGCRegion` / `GCSettings.LatencyMode` / `GC.AllocateArray<T>(length, pinned: true)`
- **GC 模式**:Workstation GC(桌面,单线程)vs Server GC(服务端,多线程并行,更高吞吐)
- **Background GC**(.NET 4+):Gen2 回收时 Gen0/1 仍可分配,避免 Full GC 阻塞整个进程
- **6 大误区**:手动 `GC.Collect()` 通常有害 / 析构函数(Finalizer)导致对象升代 / LOH 不压缩导致碎片 / 钉子对象破坏 GC 效率 / 引用类型与 GC 关系 / `GC.TryStartNoGCRegion` 必须配套 `EndNoGCRegion`
- **Q3 实战 WPF 实时行情 5 层零 GC 架构**:
- UDP 网络层(1 万 tick/s × 1000 行情 = 1000 万/秒消息)
- 解析层(struct 零分配 + `Span<T>` 切片)
- 聚合层(环形缓冲区 + 池化对象)
- 调度层(`Dispatcher.BeginInvoke` + 60 fps 节流)
- UI 层(`Freezable` 冻结集合 + `OnRender` 重写 + `DrawingVisual`)
- **诊断工具**:`dotnet-counters`(轻量采样)/ PerfView(深度事件追踪)/ `GC.GetTotalAllocatedBytes` / `GC.GetTotalMemory`
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
JIT 编译与 RyuJIT —— Day 9
---
## 📊 Day 9 教学记录(详细)
### 今日主题
JIT 编译与 RyuJIT(双层编译模型 / RyuJIT vs 旧 x86 JIT / Tiered Compilation 3 层 / Dynamic PGO / ReadyToRun (R2R) / WPF 冷启动 5 层优化架构)
### 教学重点
- **双层编译模型**:C# → Roslyn → IL → RyuJIT → 机器码(vs 静态 AOT 编译)
- **JIT 时机**:方法首次调用懒编译 + 进程内缓存 + 不跨进程共享(4 个原因:硬件/SIMD 假设 / OS 假设 / 安全 / 配置依赖)
- **RyuJIT vs 旧 x86 JIT 6 维对比表**:
- 位数(32 vs 64)
- SIMD(无 vs AVX-512 完整)
- 代码密度(松散 vs 紧凑 -30%)
- 编译速度(慢 vs 快)
- 代码质量(保守 vs 激进 SSA IR)
- Tiered 支持(无 vs 有)
- **3 个开发者可控 JIT 入口**:
- `RuntimeHelpers.PrepareMethod(methodHandle)` 提前 JIT(启动后预热关键路径)
- `[MethodImpl(MethodImplOptions.AggressiveOptimization)]` 方法级全局优化开关
- `DOTNET_JITTieredCompilation=0/1` 环境变量 + `ProfileOptimization` 多核并行
- **Tiered Compilation 3 层详解**:
- **Tier 0 Quick JIT**(~30 次调用阈值):关闭内联/循环展开/向量化 → 启动期快速生成可执行代码
- **Tier 1 Full Optimization**:完整 SSA 优化 + 内联 + SIMD 向量化
- **Tier 2 PGO**(.NET 7+ Dynamic PGO 增强):运行时 Profile 采样 → 热路径激进内联 + 冷分支外提 + 虚调用去虚化(`Guarded Devirtualization`)
- **ReadyToRun (R2R)**:
- 发布时预编译 IL + 本地机器码到 PE 文件(支持多架构 PE)
- 启动即加载机器码,等同 Tier 0 质量但**零编译延迟**
- 与 NativeAOT 本质不同(保留 JIT 灵活性 + Runtime 反射能力)
- **4 个诊断工具**:
- `dotnet-counters`(`System.Runtime` 计数器,看 `jit-time`)
- `dotnet-trace`(事件追踪 + 采样热点)
- PerfView(Windows 深度,"JIT Stats" + "CPU Stacks" 视图)
- `RuntimeHelpers.PrepareMethod` 启动后主动预热
- **WPF 冷启动典型分布**:JIT 占 30-50%(头号瓶颈)/ WPF BAML 解析 15-20% / XAML 元素实例化 10-15% / 首帧布局渲染 10-15%
- **Q3 实战 5 层优化架构**:
1. **R2R**:启动期 JIT 时间下降 40-60%(包体积 +30-50%)
2. **Tiered 调优**:缩短启动期可见卡顿
3. **PrepareMethod 预热**:用户首次操作卡顿下降 80%
4. **ProfileOptimization 多核并行**:多核 CPU JIT 下降 50%
5. **渲染线程分离 + 延迟加载**:首屏可见时间下降 30-50%
- **6 个绝对不能做的反模式**:
- 启动期关闭 GC / NoGCRegion(OOM 风险)
- 强制所有方法 Tier 1(启动期 CPU 密集)
- 用 `MethodInfo.Invoke` 替代直接调用(反射 >> JIT 开销)
- **WPF 不支持 NativeAOT**(截至 .NET 8)—— 反射 + BAML 依赖 Runtime
- 使用 NGen.exe(已淘汰,被 R2R 取代)
- 启动期 `Task.WhenAll` 加载所有服务(CPU 抢占拖慢首屏)
- **关联知识网络**:
- Day 1(值类型):RyuJIT 对 `struct` 展开/避免装箱优化
- Day 2(泛型):泛型 + JIT 共同决定 `List<int>` vs `List<string>` 性能
- Day 4(async):JIT 内联 async 状态机 `MoveNext`,async 性能关键
- Day 6(反射):`MethodInfo.Invoke` 走 JIT 内部动态方法编译路径
- Day 7(Span):JIT Tier 1+ 自动向量化 `Span<T>` 循环为 AVX 指令
- Day 8(GC):JIT 生成的代码 + GC 协同(栈分配、Boehm 标记)
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
异常处理机制(栈展开 / SEH)—— Day 10
---
## 📊 Day 10 教学记录(详细)
### 今日主题
异常处理机制(CIL Exception Handling Table / Windows SEH / 栈展开 / 零开销哲学)
### 教学重点
- **核心**:异常处理是 CIL Exception Handling Table(编译期生成到 PE)+ Windows SEH(运行时派发)的双层抽象
- **CIL 异常表 4 字段**:`Flags`(Catch/Filter/Fault/Finally)/ `TryOffset`+`TryLength`(try 块 IL 范围)/ `HandlerOffset`+`HandlerLength`(handler IL 范围)/ `ClassToken` 或 `FilterOffset`
- **关键 IL 指令**:`leave`(必须代替 `br`,保证 finally 触发)/ `rethrow`(opcode 0xFE 0x1A,保留原始栈)/ `endfinally`
- **零开销哲学精确含义**:
- ✅ 正常路径(无异常抛出):CPU 完全无额外开销(JIT 不写寄存器、不做检查)
- ❌ 异常路径:~1-10 μs(创建 Exception 对象 Gen0 分配 + 栈展开 + 表查找 + 触发 finally)
- ⚠️ finally 中大量代码:JIT 必须内联展开到所有 leave,代码膨胀拖累 I-Cache
- **SEH vs Itanium ABI 7 维对比**:
- 注册机制:静态表(编译期) vs 动态栈帧(throw 时构造)
- 栈展开时机:**找到 handler 后才展开** vs throw 后立即展开
- 硬件异常支持:✅ 原生(访问违例/除零) vs ❌ 不支持
- 调试器集成:✅ First/Second Chance vs ❌ 不支持
- **`throw` vs `throw ex`**:IL 层分别是 `rethrow` 和 `ldloc.0; throw` —— 前者保留原始 StackTrace,后者重置到 throw 行
- **First Chance vs Second Chance Exception**:
- First Chance:异常**首次抛出时**触发,CLR + 调试器订阅者接收(VS 的"异常设置"窗口)
- Second Chance:异常未被任何 handler 捕获、即将终止进程时触发(dump 抓取时机)
- **6 大误区**:
1. `throw ex` vs `throw` —— 栈重置陷阱
2. catch 后吃掉异常 —— No-op Catch 调试地狱
3. 用异常做正常控制流 —— 失败场景 `int.Parse + try/catch` 比 `int.TryParse` 慢 50-100 倍
4. finally 不一定执行 —— StackOverflowException(.NET Core 2+ 完全不可 catch)/ FailFast / kill -9 / OOM 提前崩溃 / AppDomain 卸载
5. catch 顺序错误 —— 多态陷阱(`catch (Exception)` 永远先匹配)
6. `AggregateException` 只属于 TPL —— `Task.WhenAll().Wait()` 包装,业务层应 `Flatten()` 处理
- **零开销断言 4 方案对比**:
- `Debug.Assert` —— Release **完全移除**(条件编译)
- `Contract.Requires<T>` —— Release **完全移除**(Code Contracts,仅 .NET Framework,已废弃)
- 自定义 `Guard.AgainstNull(x)` —— Release **保留**(强制)
- .NET 7+ `Throw.IfNull(x)` —— Release **保留**(推荐)
- **失败处理 4 方案对比**:
- `try/catch`(异常流):性能(成功零开销/失败 1-10 μs)、错误信息丰富、生态友好,但强制处理弱
- `Result<T>`(Rust 风格返回值):性能(成功 1ns/失败 1ns)、强制处理,但生态弱
- `OneOf<T1,T2>` 库:强制处理 + 多态错误,但引入第三方 + 装箱风险
- `Try*` 模式(TryParse):零开销,但 bool 强制弱 + 错误信息弱
- **Q3 实战 WPF 金融交易终端 5 层异常架构**:
- **网络层**:捕获 SocketException/TimeoutException,转换为 TransientOrderException(业务异常);其他异常不捕获,上抛
- **业务层**:catch TransientOrderException → Retry / catch OrderValidationException → 拒绝订单 / catch Exception → 告警 + 兜底
- **调度层**:Dispatcher 异常隔离,UI 更新失败不影响业务正确性
- **聚合层**:异常采样上报(避免高频异常拖垮遥测管道)
- **遥测层**:Channel 背压 + 后台线程消费(异步上报,不阻塞业务路径)
- **实战策略**:
- 业务层用 `try/catch`(异常即业务事件)
- 核心热路径用 `TryParse` 风格(避免异常拖慢 P99)
- 跨层边界用 `Result<T>`(强制调用方处理)
- 多态错误用 `OneOf<T1,T2>`(业务有多种合理失败)
- **5 个反模式**:catch 代替 if / 无脑包装 / 空 catch / finally 中抛异常 / 异步中吞异常
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
线程池与 TaskScheduler —— Day 11
---
---
## 📊 Day 11 教学记录(详细)
### 今日主题
CLR 线程池与 TaskScheduler(`ThreadPool` / Work-Stealing / Hill Climbing / Starvation Avoidance / `TaskScheduler` / `LongRunning` / WPF UI 调度)
### 教学重点
- **核心**:CLR ThreadPool 是进程级共享线程池,由 4 大设计哲学驱动——线程复用(~100μs 避免 + 1MB 栈 + 句柄开销)/ 自适应并发(Hill Climbing → Starvation Avoidance)/ 工作窃取(本地 LIFO + 偷取 FIFO 缓存友好)/ 注入控制
- **ThreadPool 4 大设计哲学**:
1. **线程复用**:避免反复创建/销毁线程(~100μs + 1MB 栈 + 内核对象 + 句柄表项 + GC 析构)
2. **自适应并发**:min=CPU 核心数, max=32,767,根据负载动态注入/回收
3. **工作窃取(Work-Stealing)**:每个 worker thread 优先消费本地队列,空闲时从其他线程的本地队列尾部窃取
4. **注入控制**:早期 Hill Climbing 爬山算法(采样/调步/震荡缺陷)→ .NET Core 3+ Starvation Avoidance 主动监控饥饿时间 ~500ms
- **Work-Stealing 算法详解**:
- **本地队列**:环形数组(MPSC,Multi-Producer Single-Consumer),默认长度 16
- **本地 LIFO**:自己刚 push 的热数据,缓存命中
- **偷取 FIFO**:对方的旧数据,**避免对方已预热的缓存行被偷走**
- **Hill Climbing 伪代码**:每 1-10 秒采样吞吐 → 升降线程数 → 缺陷(响应慢/震荡/长尾不友好)
- **Starvation Avoidance 伪代码**:worker 等待全局队列 > 500ms → 立即注入新线程 → 优势(突发友好/无震荡/长尾友好)
- **`TaskCreationOptions.LongRunning` 底层**:CLR 内部 `new Thread(IsBackground=true).Start(action)`,独立线程不入池,释放池容量给短任务
- **长任务 3 大危害**:worker 线程被占满饥饿风险 / 线程注入失控上下文切换成本反升 / GC 根膨胀扫描成本高
- **`TaskScheduler` 解耦作用**:把"线程池"从"线程执行"中抽离——同一 `Task.Run` 可调度到 ThreadPool / DispatcherSynchronizationContext / 自定义调度器 / 调试可视化
- **WPF UI 调度核心**:
- UI 线程默认有 `DispatcherSynchronizationContext`,await 自动捕获
- 续作自动回到 UI 线程(无需显式 `Dispatcher.Invoke`)
- 库代码一律 `.ConfigureAwait(false)` 避免上下文捕获开销
- **真异步 I/O 关键**:HttpClient.SendAsync / Stream.ReadAsync / Socket.ReceiveAsync 走 IOCP(I/O 完成端口),**不占 worker thread**——这是线程池饥饿的真正解药
- **6 大误区**:
1. `Task.Run(...).Result/.Wait()` 在 UI 线程死锁
2. 假异步包装(`Task.Run(() => File.ReadAllText)`)浪费线程池
3. 大量并发 I/O 调大 `SetMinThreads` 错误(I/O 走 IOCP)
4. `LongRunning` 当作优化开关(滥用 = 退化到 new Thread)
5. `Task.Delay(1000).Wait()` 占线程池(应该 await 或 Thread.Sleep)
6. `Task.Run` 内捕获 SynchronizationContext(依赖上下文易死锁)
- **Q3 实战 WPF 实时看板 5 层架构**:
- **UI 层**:DispatcherSynchronizationContext 隐式捕获 + IProgress<T> 自动回 UI + CancellationTokenSource 触发
- **调度层**:Task.WhenAll + 独立 Task 包装(5 个全并发,扩展 50+ 用 SemaphoreSlim 限流)
- **业务层**:每个 Task 独立 try/catch(异常隔离) + HttpClient.SendAsync 真异步
- **进度聚合层**:Channel<ProgressReport> 背压解耦 + Dispatcher 节流到 60fps + Progress<T> 内部节流
- **遥测层**:Channel 消费失败上报 + 后台线程异步上报不阻塞业务
- **性能对比表**:
| 方案 | 5 个服务总耗时 | UI 卡顿 | 线程池占用 |
|------|---------------|---------|-----------|
| 同步顺序调用 | 1000 ms | 1000 ms 完全卡死 | 1 worker |
| `Task.Run × 5 + .Wait()` | 200-300 ms | **死锁 ❌** | 5 worker + UI 卡 |
| `Task.WhenAll + ConfigureAwait` | **200-300 ms** | **60 fps 流畅** | **0 worker(IOCP)** |
| `SemaphoreSlim(3)` 限流 | 300-400 ms | 60 fps 流畅 | 0 worker(IOCP) |
- **关键洞察**:HttpClient.SendAsync 走 IOCP,**即使 500 个并发请求也是零 worker 占用**。线程池饥饿的真正杀手是 `Task.Run` + 同步 I/O 滥用
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
锁机制(Monitor / SemaphoreSlim / AsyncLock)—— Day 12
---
---
## 📊 Day 12 教学记录(详细)
### 今日主题
锁机制深度剖析(`lock` / `Monitor` / `SemaphoreSlim` / `AsyncLock` / `ReaderWriterLockSlim` / 内存模型语义)
### 教学重点
- **核心**:锁是同步原语——CPU 原子指令(`Interlocked`)→ 用户态自旋(`SpinLock`)→ 混合锁(`Monitor`/`SemaphoreSlim`)→ 内核态锁(`Mutex`/`Semaphore`)→ 跨进程内核(命名 Mutex/Semaphore)—— 性能/通用性递进
- **`lock` 是 `Monitor` 语法糖**:编译器生成 `try { Monitor.Enter(obj, ref lockTaken); ... } finally { if (lockTaken) Exit(); }`,`lockTaken` 是 .NET 4.0 引入的异常安全参数
- **7 种同步机制 6 维对比**:性能(ns/μs/ms)/ 跨进程(❌/✅)/ 异步(❌/✅)/ 底层机制(CPU指令/对象头/内核对象)/ 持锁线程要求 / 典型场景
- **`lock` 不能跨 await 的 3 大原因**:
1. 线程亲和性(`Monitor.Exit` 必须由持锁线程调用,await 后续可能换线程)
2. 永久死锁风险(await 期间锁被另一线程获取,原线程回来等待自己持的锁)
3. finally 不可靠(早期 .NET Framework 的 `ThreadAbortException` 会中断 finally)
- **CLR 对象头 Sync Block Index**:
- 64 位对象头:前 4 字节 Sync Block Index,后 8 字节 MethodTable Ptr
- 索引 = 0 未锁定 / -1 Thin Lock(线程 ID + 递归计数存对象头)/ > 0 Fat Lock(指向 Sync Table 条目)
- **Thin → Fat 升级**:另一线程进入时,CLR 分配 Sync Block Entry,对象头指向 Entry
- **内存屏障语义**:
- `Monitor.Enter` = **acquire fence**(之后读写不能前移)
- `Monitor.Exit` = **release fence**(之前读写不能后移)
- `Monitor.Enter + Exit` = **full fence**
- `volatile` 字段 = 字段读写自带 acquire/release fence
- `Volatile.Read/Write` = 显式 acquire/release
- `Interlocked.MemoryBarrier()` = full fence
- **.NET 内存模型**:x86/x64 强模型(天然有序)/ ARM64 弱模型(需要显式 acquire/release)—— 业务代码应**始终**用 `volatile`/`lock`/`Interlocked`,不依赖特定架构
- **`ReaderWriterLockSlim` 递归陷阱**:
- 默认 `LockRecursionPolicy.NoRecursion`,同线程递归进入抛 `LockRecursionException`
- `SupportsRecursion` 启用后:读读递归 ✅ / 读写升级 ✅ / 写读降级 ⚠️ / 写写递归 ✅
- 升级规则:`EnterReadLock` → `EnterWriteLock` 直接抛 `LockUpgradeException`(除非 SupportsRecursion)
- **自建 `AsyncLock` 生产模板**:
- 内部 `SemaphoreSlim(1,1)` + 预创建 `_releaser` Task(避免每次 lock 分配)
- 快速路径(未争用)直接返回已完成的 task
- 慢速路径用 `ContinueWith` 在 ThreadPool 上创建 releaser
- 现成方案:`Nito.AsyncEx.AsyncLock`
- **`Monitor.Wait` 语义**:在 lock 内调用会**原子地释放锁 + 阻塞当前线程 + 加入等待队列 + 等待 `Pulse/PulseAll`**,被唤醒后**重新获取锁**继续执行
- **Q3 实战 WPF 实时行情订阅 5 层架构**:
- **数据源层**:4 个行情源(CTP/LTS/Binance/Simulated)→ 各自推送线程 → `Channel<Tick>` bounded=10000(背压)
- **聚合层**:单一后台线程消费 Channel → `AsyncLock` 写 OrderBook
- **业务层**:`AsyncLock` 保护 `Dictionary<string, Order>` + 重建 `ImmutableArray<Order>` 快照
- **持久化层**:`PeriodicTimer` 每 100ms 读快照 → 异步写 SQLite(不持业务锁)
- **UI 层**:Dispatcher 60fps + `CompositionTarget.Rendering` 帧对齐 + 差异检测
- **5 大陷阱**:
- T1:UI 线程死锁(`_book.GetSnapshot()` 内 `.Result/.Wait()`)
- T2:Channel 背压失效(`Unbounded` + 多生产线程 → OOM)
- T3:持久化层锁订单簿(await IO 时间过长)
- T4:聚合线程单点故障(异常被吞)
- T5:Dispose 时死锁(`WaitAsync` 没传 `CancellationToken`)
- **4 种方案对比**:
- A:`Monitor` 全程 —— 最简单但跨 await 不支持 ❌
- B:`AsyncLock` + `SemaphoreSlim` —— MVP 阶段推荐 ✅
- C:`ReaderWriterLockSlim` + `ImmutableArray` —— 读多写少 >10:1
- D:`Channel<T>` + 不可变 + Lock-Free —— **生产级推荐**(金融终端工业标准)
- **8 个常见误区**:
- `lock(this)/lock(typeof)/lock(string字面量)` —— 灾难性
- lock 内 await —— CS1996 警告 + `SynchronizationLockException`
- `volatile` 不能替代锁 —— `x++` 是 read-modify-write 非原子
- `SemaphoreSlim` 必须传 CancellationToken + try/finally Release
- `ReaderWriterLockSlim` 升级规则(读→写直接抛异常)
- finally 中 await 后释放锁(拉长持锁时间)
- 用 lock 限流(应该用 `SemaphoreSlim(maxCount)`)
- `SemaphoreSlim` 不 Dispose 会泄漏资源
- **关联知识网络**:
- Day 4(async/await):await 跨线程 → `lock` 不能跨 await → 必须用 `SemaphoreSlim`
- Day 8(GC):锁对象越小越好(Gen0 快速回收)
- Day 10(异常):try/finally 释放锁是基础设施
- Day 11(ThreadPool):`SemaphoreSlim.WaitAsync` 内部用 TCS 复用线程池
- Day 7(Span):`lock` 不能保护 `ref struct`(不能装箱为 `object`)
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
并行编程(Parallel / PLINQ / Channel<T> / TPL Dataflow)—— Day 13
---
## 📊 Day 13 教学记录(详细)
### 今日主题
.NET 并行编程全景(`Parallel.For/ForEach` / `PLINQ` / `Channel<T>` / `TPL Dataflow` / `Task.WhenAll` 5 大原语对比 / 分区算法 / 背压协议 / WPF 5000 张 4K 图像 5 层流水线实战)
### 教学重点
- **5 大并行原语层级表**(抽象层次/并行模型/调度开销/适用场景):
- `Parallel.For/ForEach` —— 数据并行(DOP)/ ~10-100 ns/项 / CPU 密集迭代独立
- `PLINQ` —— 数据并行 + 流水线 / ~100-500 ns/项 / 查询式可分区
- `Channel<T>` —— 生产者-消费者 + 背压 / ~50-200 ns/项 / 异步流水线解耦
- `TPL Dataflow` —— 块网络(Mesh)/ ~200-500 ns/项 / 多阶段流水线扇出扇入
- `Task.WhenAll` —— 显式并发 / 自定义 / 已知 N 个独立 Task
- **3 个并行设计目标**:自动分区 + 自动负载均衡 + 背压控制
- **Amdahl 定律警告**:串行部分 S 决定最大加速比 `1/S`
- **4 个最小代码示例**:
- `Parallel.For(0, 1000, i => ...)` + `ConcurrentBag<T>`
- `AsParallel().Where(...).OrderByDescending(...).Take(100).ToList()`
- `Channel.CreateBounded<Tick>(10000, FullMode=Wait)`
- Dataflow `TransformBlock → TransformBlock → ActionBlock` 链接
- **10 个反模式表**:Parallel 处理 I/O / PLINQ 处理小数据集 / BlockingCollection / 共享状态无锁 / 不处理 AggregateException / Parallel.For 默认有序 / Channel Unbounded / Dataflow 不设 BoundedCapacity / 并行 lambda 内 await / 并行度 = int.MaxValue
- **`Parallel.For` 分区算法 3 种**:
- **静态分区**(默认):等分成 K=ProcessorCount 段 → 缓存友好 / 负载不均 → 均匀任务
- **动态分区**:`Partitioner.GetDynamicPartitions()` → 负载均衡 / 缓存差 / 同步开销 → 不均任务
- **Chunk 分区**(推荐折中):每段 N=ProcessorCount 项 → 平衡两者
- 实测数据:10000 项订单 1-100ms 随机,静态 1.2s / 动态 700ms / Chunk 750ms
- **数据并行 vs 流水线并行**:
- **数据并行**(`Parallel.ForEach`):N 个独立项 → K 个 worker 同时处理(空间换时间)
- **流水线并行**(PLINQ 内部):各阶段独立并行(`Where → Select → OrderBy`),项 1 完成 Where 立即进入 Select(**Stage-Level Parallelism**,即使少量数据也能提速)
- ⚠️ **`OrderBy` / `Aggregate` / `Take` 强制串行**(需全局顺序)
- **PLINQ = 数据并行 + 流水线并行**(二维并行杀手锏)
- **Channel 背压 5 策略对比**:
- `Wait`(生产者 await 阻塞)—— **金融首选**(订单零丢失)
- `DropOldest`(丢队首)—— 日志流
- `DropNew`(丢队尾)—— 监控采样
- `DropWrite`(丢当前)—— 非关键事件
- `Throw`(抛 ChannelClosedException)—— 测试
- **Q3 实战 WPF 5000 张 4K 图像 5 层流水线**:
- **架构**:Loader(4w, I/O) → Resizer(8w, CPU) → Sharpener(8w, CPU) → Encoder(4w, CPU) → Saver(2w, I/O)
- **worker 数差异原理**:CPU 紧层满载 `ProcessorCount` / I/O 层 4-2(磁盘/网络瓶颈)
- **生产代码 5 层 Channel + Dispose + CancellationToken 透传 + Writer.Complete 优雅关闭**
- **5 种方案对比表**:Parallel.ForEach 全程 ❌(OOM + UI 死锁 / ~45s)/ PLINQ ❌(同 / ~50s)/ Dataflow ✅(~30s)/ Channel+Task ✅(~28s)/ Channels+Dataflow ✅✅(~25s,**生产首选**)
- **内存控制 4 策略**:Bounded Channel + 及时 Dispose + Span+ArrayPool + 流式处理
- **3 层取消防护**:CancellationToken 透传 + Writer.Complete + OperationCanceledException 清理
- **性能预期**:整体 ~10 min(流水线瓶颈 = 最慢层 Saver 4min)vs 串行 ~80 min = **8x 加速**
- **5 大陷阱**:UI 线程死锁 / 内存爆炸 / BoundedCapacity 缺失 / Bitmap 不 Dispose / 取消未清理
- **3 道面试题答案要点**:
- **Q1**:5 大原语 5 维对比表 + 区分并行模型(数据并行/流水线并行)+ UI 线程禁用 `Parallel.For`
- **Q2**:静态 vs 动态 vs Chunk 分区 trade-off + 数据并行 vs 流水线并行本质区别 + Channel `Wait` 是金融唯一选择
- **Q3**:5 层流水线架构 + 完整生产代码 + 5 方案对比 + 内存控制 + 性能预期 + 5 陷阱
- **10 倍扩展思考**:多机分布式 / 内存映射文件 / GPU 加速(DirectX/Vulkan 4K 缩放 20-50x)/ 流水线启动延迟 vs 短任务陷阱
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
WPF 架构总览(PresentationFramework / PresentationCore / WindowsBase + XAML 编译原理 BAML + InitializeComponent 反射 vs 编译期生成 + WPF 依赖 Runtime 不能 NativeAOT)—— Day 14(**Phase 3 开篇**)
---
## 📊 Day 15 教学记录(详细)
### 今日主题
WPF 依赖属性(DependencyProperty)核心(CLR 9 大局限 / 11 级优先级 / 3 大回调 / `SetValue` vs `SetCurrentValue` / `PasswordStrengthIndicator` 实战)
### 教学重点
- **核心抽象**:DP = `static readonly DependencyProperty` 全局单例标识符 + `DependencyObject` 内部 `EffectiveValueEntry[]` 懒分配动态存储。**DP 不是字段,是值解析框架** —— 每次 `GetValue` 都走 11 级优先级查找 + 表达式求值
- **CLR 9 大局限**:值继承 / 数据绑定 / 动画 / 样式 Setter / 模板 TemplatedParent / 强制值 / 验证回调 / 变更通知 / 资源引用 —— 9 项全部 ❌
- **WPF 不抛弃 CLR 包装 3+1 原因**:
1. 编译时类型检查(`Value="abc"` 编译期报错 vs DP 运行时抛 `XamlParseException`)
2. IDE IntelliSense + XAML 设计器智能提示
3. 重构友好(rename 同步所有引用)
4. 隐藏原因:CLR 包装是 XAML 解析器发现属性的入口(XAML 只认 `public` CLR 属性 + 匹配 DP 静态字段)
- **`EffectiveValueEntry` 懒分配**:1000 个 Button 只设 10 个 Width → 只有 10 个槽位被分配。**未设置的 DP 零内存占用**
- **DP 全局单例**:`Button.WidthProperty` 整个 AppDomain 唯一,所有 Button 共享标识符,只是各自 `EffectiveValueEntry` 不同
- **完整 `NumericUpDown` 生产代码**:
- `ValueProperty` 注册(`AffectsRender` + `BindsTwoWayByDefault`)
- 3 大回调完整签名(`ValidateValue` / `CoerceValue` / `OnValueChanged`)
- `Min`/`Max` 变更触发 `CoerceValue(ValueProperty)` 联动
- `DefaultStyleKeyProperty.OverrideMetadata` 指定默认样式
- **11 级值解析优先级**(核心铁律):
- 1️⃣ Coerce 最高(任何来源都被 Coerce)
- 2️⃣ 动画(`Storyboard` 覆盖本地值)
- 3️⃣ 本地值(`SetValue` 显式设置)
- 4️⃣ TemplatedParent(模板内属性)
- 5️⃣ 样式触发器(Trigger 覆盖 Setter)
- 6️⃣ 样式 Setter(覆盖主题)
- 7️⃣ 主题样式触发器
- 8️⃣ 主题样式 Setter
- 9️⃣ 继承值(`Inherits=true` 沿视觉树向下传)
- 🔟 默认值最低(兜底)
- **3 大回调执行顺序**:`ValidateValue`(门卫,非法抛 `ArgumentException`)→ `CoerceValue`(矫正师,调整到合法范围)→ `PropertyChanged`(广播员,通知值已变更)
- **`SetValue` 完整事件链伪代码**:Validate → Coerce → 短路检查(同值不重复触发)→ 写 `EffectiveValueEntry` → PropertyChangedCallback → `OnPropertyChanged(dp.Name)` → `BindingExpression.UpdateSource()` 双向绑定反向通知
- **深度解析**:DP 不是字段是**表达式** —— `EffectiveValueEntry` 含 `Expression` 字段表示未求值的绑定/动态资源,WPF 在 `Expression` 层拦截实现 Binding 改写 DP(**不需要 `SetValue`**)
- **`SetValue` vs `SetCurrentValue` 5 维对比**:
- 写入层级:本地值 vs 当前值
- 打断动画:✅ vs ❌
- 打断数据绑定:✅ vs ❌
- 打断样式触发器:✅ vs ❌
- 触发 PropertyChanged + Coerce:都 ✅
- **关键洞察**:`SetValue` = 完全掌控(清空所有其他源)/ `SetCurrentValue` = 柔和更新(只改当前展示值)
- **`RegisterReadOnly` + `DependencyPropertyKey` 模式**:
- `DependencyPropertyKey` 持有 `SetValue` 权限(私有静态字段)
- 公开 `DependencyProperty` 给外部只读 `GetValue`
- 避免外部绕过 Coerce 直接 `SetValue`
- **`ClearValue` vs `SetValue(defaultValue)` 铁律**:
- `ClearValue` = 删除本地值(回到继承/默认)
- `SetValue(defaultValue)` = **显式本地值**(优先级 3️⃣ 最高之一)
- 二者完全不同!
- **`OverrideMetadata` vs `AddOwner` 区别**:
- `OverrideMetadata` = 改变继承类元数据(同 DP,**同 Owner**)
- `AddOwner` = 把别人注册的 DP 借到自己类上(**多 Owner**)
- **`RegisterAttached` 附加属性**:
- 是"寄生"在其他类上的 DP(如 `Grid.Row`)
- **不能写 CLR 包装**(用静态 `GetXxx`/`SetXxx` 方法访问)
- **Q3 `PasswordStrengthIndicator` 完整生产代码**:
- `PasswordProperty`(`AffectsRender` + `BindsTwoWayByDefault`)
- `StrengthProperty`(`RegisterReadOnly` + `DependencyPropertyKey` + Coerce 强制空密码为 Weak)
- `ForegroundProperty`(`AddOwner` 到 `Control.ForegroundProperty` + `Inherits`)
- `OnPasswordChanged` 用 `SetCurrentValue(StrengthPropertyKey, ...)`(**绝不**用 `SetValue`,否则 Coerce 失效)
- `CoerceStrength` 读 `ctrl.Password` 当前值判断(不是 `baseValue`)
- 强度算法(长度 < 6 Weak / 1 项 Medium / 2+ 项 Strong)
- **5+3 大生产级陷阱**:
- T1 双向绑定死循环 → 单向 `Mode=OneWay`
- T2 Coerce 无限递归 → 直接 `return` 不 `SetValue`
- T3 `SetValue` 清空 Coerce → 用 `SetCurrentValue`
- T4 Strength 未只读保护 → `RegisterReadOnly` + 私有 `Key`
- T5 Foreground 缺 `Inherits` → 标记 + `AddOwner`
- T6(隐藏)异步计算防抖 → `CancellationTokenSource`
- T7(隐藏)`OnPasswordChanged` 内不抛异常 → 避免打断 WPF 状态机
- T8(隐藏)`Foreground` 用 `AddOwner` 而非 `new PropertyMetadata` → 避免双 Foreground DP
- **关联知识网络**:
- **Day 14 WPF 架构** → DP 注册走 `static readonly` 反射 → WPF 不能 NativeAOT 第二大根因
- **Day 11 ThreadPool** → `DispatcherSynchronizationContext` 让 `OnPasswordChanged` 自动在 UI 线程执行
- **Day 6 反射** → `DependencyPropertyDescriptor` 是反射访问 DP 的元数据描述器
- **Day 8 GC** → `EffectiveValueEntry[]` 按需分配,未设置的 DP 零内存占用
- **未讲 Day 17 路由事件** → `RoutedEventArgs.OriginalSource` 本质是 DP
- **未讲 Phase 4 数据绑定** → `Binding` 目标必须是 DP
- **未讲 Phase 4 样式/动画** → `<Setter>` 和 `Storyboard` 只能操作 DP
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
路由事件(Routed Event)—— Day 16(3 种路由策略 Bubble/Tunnel/Direct / `RoutedEventArgs.Source` vs `OriginalSource` / `EventManager.RegisterRoutedEvent` + `RaiseEvent` / 附加事件 / 5 大使用陷阱)
---
## 📊 Day 16 教学记录(详细)
### 今日主题
WPF 路由事件(Routed Event)核心(CLR 事件 vs 路由事件本质区别 / 3 种路由策略 / Source vs OriginalSource / 视觉树 vs 逻辑树 / Handled + handledEventsToo / EditableUserCard 双模式实战)
### 教学重点
- **核心抽象**:路由事件 = 能在元素树(视觉树)中按特定方向传播的事件 = `static readonly RoutedEvent` 全局单例 + `RoutedEventArgs` 携带 Source/OriginalSource/Handled + `EventManager.RegisterRoutedEvent` 注册机制
- **3 种路由策略核心铁律**:
- **Bubble**(冒泡):Source → 根,**无前缀**(Click / MouseLeftButtonDown)——最常用
- **Tunnel**(隧道):根 → Source,**Preview 前缀**(PreviewMouseDown / PreviewKeyDown)——父容器提前拦截
- **Direct**(直接):不传播(Loaded / Unloaded)——类似 WinForms
- **完整传播顺序图**(Button 在 StackPanel → Grid → Window 中被点击):
```
Window.PreviewMouseDown ← Tunnel(根向下)
└ Grid.PreviewMouseDown
└ StackPanel.PreviewMouseDown
└ Button.PreviewMouseDown ← Tunnel 终点
└ Button.MouseLeftButtonDown ← Direct 触发(Source)
└ StackPanel.MouseLeftButtonDown ← Bubble(Source 向上)
└ Grid.MouseLeftButtonDown
└ Window.MouseLeftButtonDown
```
- **4 大铁律**:Tunnel 总在 Bubble 之前 / Button 自己既是 Tunnel 终点又是 Bubble 起点 / Tunnel 数量=树深度 / Bubble 数量=树深度+1
- **3 步骤生产代码**:`RegisterRoutedEvent`(名字+策略+handler类型+owner)→ CLR 包装(add/remove 用 AddHandler/RemoveHandler)→ RaiseEvent(必须先 base.OnXxx(e))
- **Source vs OriginalSource 对比表**:
- `Source`:逻辑源,谁 RaiseEvent 谁就是 Source(触发代码决定)
- `OriginalSource`:物理源,InputManager 命中测试的实际元素(命中测试决定)
- 场景 1:Button 内 TextBlock 模板 → Source=Button / OriginalSource=TextBlock(最常见)
- 场景 2:自定义 ControlTemplate 内 Border → OriginalSource=Border(模板嵌套)
- 场景 3:OnApplyTemplate 动态元素 → Source 任意/OriginalSource 命中测试
- **视觉树 vs 逻辑树双树架构**:
- **逻辑树**(Logical Tree):XAML 声明层级,用于 DataContext 继承 / 资源查找 / ItemsControl 容器识别
- **视觉树**(Visual Tree):实际渲染的所有视觉元素(含模板内部 / Adorner 装饰器)
- **路由事件沿视觉树传播**,**数据绑定沿逻辑树解析**——这是两个完全不同的遍历路径
- **Handled 机制 + handledEventsToo 父容器精确拦截**:
- `e.Handled = true`:默认停止冒泡
- `AddHandler(routedEvent, handler, handledEventsToo: true)`:外层仍能接收已处理事件
- XAML `+=` / C# `event +=` **无法传 handledEventsToo**,必须用 `AddHandler`
- **附加事件(Attached Event)vs 路由事件**:
- 附加事件:"寄生"在其他类上的路由事件(如 `Mouse.MouseDown` 给任意 UIElement)
- XAML 写 `MouseHelper.HoverEnter="..."` 实际是调用静态 `AddHoverEnterHandler`,C# 直接 `+=` 是错的
- **Q3 实战 EditableUserCard 双模式 ListBox**:
- **❌ Tunnel 方案 3 大致命缺陷**:Button 视觉效果丢失 / 无法阻止 Bubble 触发删除 / 点删除同时显示详情
- **✅ Bubble+Handled+handledEventsToo 完整方案**:
```csharp
// Button 内 e.Handled = true 阻止冒泡
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
e.Handled = true; // 关键
DeleteUser();
}
// ListBox 用 AddHandler + handledEventsToo 仍接收
listBox.AddHandler(Mouse.MouseDownEvent,
new MouseButtonEventHandler(OnRowClick),
handledEventsToo: true);
// FindAncestor<T> 视觉树遍历判断 OriginalSource 是否落在 Button
```
- **4 个关键技术点**:Handled 在 Button 设 / AddHandler 而非 += / OriginalSource 而非 Source / FindAncestor 而非 e.Source
- **5 种方案对比表**:Bubble+Handled ✅ 生产级推荐 / Tunnel 全局快捷键 / 附加事件第三方控件 / Adorner 拖放选择 / RoutedCommand 复杂框架
- **5 大生产陷阱**:漏 e.Handled=true / 用 += 而非 AddHandler / e.Source is Button 永远 false / FindAncestor 用逻辑树 / 异步 await 后改 Handled 不可变
- **关联知识网络**:
- **Day 15 DP** → `RoutedEventArgs.Source` 由触发代码决定,事件传播过程中 WPF 用 DP 系统解析元素位置
- **Day 14 WPF 架构** → `EventManager.RegisterRoutedEvent` 走 `static readonly` 反射注册,是 WPF 不能 NativeAOT 第三大根因
- **Day 11 ThreadPool** → 路由事件默认同步触发,`BeginInvoke` 可异步派发到 UI 线程
- **未讲 Phase 4 命令系统** → `RoutedCommand` 完全依赖路由事件机制(Execute/CanExecute 沿视觉树冒泡)
- **未讲 Phase 4 数据绑定** → `Binding.Source` 沿逻辑树解析 DataContext,与路由事件视觉树路径完全不同
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
视觉树与逻辑树(Visual Tree vs Logical Tree / `VisualTreeHelper` / `LogicalTreeHelper` / 模板展开与视觉子树 / `FindAncestor` / `VisualBrush` / 装饰器 Adorner / 命中测试 HitTest)—— Day 17
---
## 📊 Day 14 教学记录(详细)
### 今日主题
WPF 架构总览(7 层程序集分层 / `WindowsBase`↔`PresentationCore`↔`PresentationFramework` 依赖关系 / XAML 编译原理 BAML / `InitializeComponent` 反射加载机制 / Milcore C++ COM 跨进程 / WPF 不能 NativeAOT 的 5 大根因)
### 教学重点
- **核心洞察**:WPF 不是一个类库,是一个**多程序集分层架构**。理解 7 层架构 + BAML 编译 + InitializeComponent 反射 + Milcore 跨进程,你对 WPF 的认知就超过了 80% 的 WPF 开发者
- **7 层程序集全景**:
- **Milcore** (milcore.dll + WindowsCodecs.dll) —— C++ COM 跨进程 + Direct3D 渲染
- **WindowsBase** —— 窗口系统无关:`DispatcherObject` / `DependencyObject` / `Freezable` / `DependencyProperty` / `WeakEventManager`
- **PresentationCore** —— `Visual` / `UIElement` / `FrameworkElement` / `Brush` / `Geometry` / 命中测试 / 布局引擎
- **System.Xaml** —— 通用 XAML 解析器(独立于 WPF):`XamlReader` / `XamlWriter` / `MarkupExtension`
- **PresentationFramework** —— WPF 顶层:`Application` / `Window` / `Page` / 全部内置控件 / `Style` / `ControlTemplate` / `DataTemplate`
- **System.Xml + System.Markup** —— XML 解析基础设施
- **应用层** —— .exe + .xaml + App.xaml.cs + .g.cs(设计器生成)
- **依赖方向图**:`PresentationFramework → PresentationCore + System.Xaml → WindowsBase`,**反向引用不存在**
- **设计哲学**:关注点分离 + 可测试性 + 渐进式复杂度。WindowsBase 与 PresentationCore 分离让底层类型不依赖具体窗口模型;PresentationCore 与 PresentationFramework 分离让 `UIElement` / `FrameworkElement` 可独立测试;System.Xaml 独立让 UWP/WinUI/WCF/WF 共享同一套解析器
- **XAML→BAML 编译 4 步**:
1. XamlBuildTask 语法解析
2. **设计时实例化**(构建期发现错误)
3. XamlWriter 序列化为 BAML 字节流
4. gzip 压缩嵌入 .exe 的 `.mresource` 区
- **`InitializeComponent` 运行时 5 步**:
1. 构造 BAML 资源 URI(`/MyApp;component/mainwindow.baml`)
2. `Application.LoadComponent(this, uri)` 调用
3. `Assembly.GetManifestResourceStream()` 打开 BAML 流
4. `Baml2006Reader` 读取字节流
5. `WpfXamlLoader.Load()` 反射调用 setter + 事件处理器(`GetEvent` + `GetMethod` + `Delegate.CreateDelegate`)
- **XAML 文本 vs BAML 二进制 6 维对比**:解析速度(BAML 5-10x 快)/ 内存占用 / 类型查找(TypeId 预解析 O(1))/ 属性设置 / 启动期 CPU / 调试体验(BAML 错误指向偏移而非行)
- **WPF 不能 NativeAOT 的 5 大根因 + IL 层表现**:
1. **BAML 反射 `Activator.CreateInstance`** —— `callvirt System.Activator::CreateInstance(System.Type)`
2. **DP 注册走 `static readonly` 字段反射** —— 剪裁器看不到运行时枚举
3. **`x:Class` + 事件处理器字符串反射** —— `GetMethod("OnButtonClick", BindingFlags)` 可能剪掉方法
4. **数据绑定 `Path` 字符串反射链** —— `Binding Path=Foo.Bar.Baz` 编译期不可见
5. **样式/模板 `BasedOn`+`StaticResource` 运行时解析** —— 资源键是字符串
- **附加 3 个根因**:附加属性 `GetXxx/SetXxx` 静态方法对反射 / `MarkupExtension` 运行时实例化 / 第三方库未标记 `[RequiresDynamicCode]`
- **WPF 应用典型体积表**:最小 Hello World 80 MB → 大型业务 160 MB → 启动时间 1.5-5 s
- **3 个替代方案(不放弃 WPF)**:
- **方案 1:R2R + Tiered PGO** ✅ 推荐 —— 启动降 30-50%,体积 +30-50%(多架构 PE)
- **方案 2:延迟加载 + AssemblyLoadContext 插件化** —— 主 exe 降 40-60%,启动降 20-30%
- **方案 3:Self-Contained + Trimmed(仅剪非 WPF)** —— `<PublishTrimmed>true</PublishTrimmed>` **不能用于 WPF**!必须 `TrimMode=link` 或仅剪业务程序集 + `[RequiresDynamicCode]` 标注
- **XAML 2006 vs XAML 2009**:2006 是默认(设计器友好 + BAML 支持)/ 2009 支持 `x:Reference` 双向绑定 / 泛型 `x:Array` / 内置 `MarkupExtension`,但 **BAML 不支持**(只能用反射 XAML)
- **Milcore 关键认知**:C++ COM 组件跨进程 IPC + Direct3D 渲染 + 软件回退。Managed 层只是 `SafeHandle` 包装,渲染问题 90% 在 Milcore 层无法直接调试
- **关联知识网络**:
- **Day 6 反射** → BAML 解析 `Activator.CreateInstance` 是 NativeAOT 失败根因
- **Day 8 GC** → Milcore 跨进程 = 托管堆与原生堆分离,需 `SafeHandle` 管理生命周期
- **Day 9 JIT** → BAML setter 调用需 JIT 内联,冷启动慢(占 15-20%),R2R 直接消除
- **Day 11 ThreadPool** → `DispatcherSynchronizationContext` 捕获让 await 自动回 UI 线程
- **Day 13 Channel** → 跨线程给 WPF 控件赋值 = `Channel<T>` + `Dispatcher.BeginInvoke`
- **未讲 Day 15 依赖属性** → DP 注册是 WPF 反射密集的第二大根因
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
依赖属性(DependencyProperty)核心 —— Day 15(CLR 属性 vs DP / GetValue/SetValue 反射 / `DependencyProperty.Register` 注册 / 元数据继承 / 值变更回调 / `CoerceValueCallback` / `ValidateValueCallback` / 强制值 / 属性值优先级 / `ClearValue` / WPF DP 系统全景 + 数据绑定 / 样式 / 动画如何依赖 DP)
---
## 📊 Day 17 教学记录(详细)
### 今日主题
WPF 视觉树与逻辑树(Visual Tree vs Logical Tree / `VisualTreeHelper` / `LogicalTreeHelper` / `OnApplyTemplate` / `FindAncestor` 缓存 / `VisualBrush` / 命中测试 / `ChartCard` 数据可视化实战)
### 教学重点
- **核心抽象**:WPF 所有 UI 元素**同时存在两棵树**里——逻辑树(XAML 声明的层级,开发者意图结构)和视觉树(实际渲染的层级,含模板内部所有元素)。两棵树**正交分离**,走完全不同的遍历路径
- **两棵树正交分离的设计动机**:
- **单一职责 + 关注点分离**:逻辑树 = DataContext 继承 / 资源查找 / 容器识别;视觉树 = 渲染 / 命中测试 / 事件传播
- **同一逻辑节点视觉树可完全不同**:同一 `Button` 在 XP 主题 = 12 个视觉子节点 / Aero = 20 个 / 自定义模板 = N 个
- **Day 16 路由事件关键洞察**:事件沿视觉树传播 / 绑定沿逻辑树解析 `DataContext` —— 两条完全不同的路径
- **4 大核心 API 对比表**:
| API | 命名空间 | 时间复杂度 | 原理 |
|-----|---------|----------|------|
| `VisualTreeHelper.GetParent(v)` | `System.Windows.Media` | **O(1)** | 字段查找(`Visual._parent`) |
| `VisualTreeHelper.GetChild(v, i)` | 同上 | O(1) | 索引访问 |
| `LogicalTreeHelper.GetParent(e)` | `System.Windows` | **O(深度)** | 递归向上找下一个 `FrameworkElement` |
| `LogicalTreeHelper.GetChildren(e)` | 同上 | O(子节点数) | 枚举 |
⚠️ `LogicalTreeHelper.GetParent` 对 `ContentElement`(如 Run/Hyperlink)必须跨层找 `FrameworkElement`
- **`FindAncestor<T>` 生产级实现 + 缓存优化**:
- ❌ 反例:每次调用从头递归(O(深度))
- ✅ 生产级:`ConditionalWeakTable<DependencyObject, AncestorMap>` 弱引用键缓存 → 命中 O(1),首次 O(深度)
- **为什么用 `ConditionalWeakTable`**:普通 `Dictionary` 强引用祖先 → 元素已视觉上离开但缓存还在 → **内存泄漏**
- **`OnApplyTemplate` 模板入口铁律**:
- `GetTemplateChild` **只能在 `OnApplyTemplate` 内调用**(模板懒加载)
- 必须 `base.OnApplyTemplate()` 触发模板事件
- 主题切换 → 旧模板视觉子树销毁 → **新模板视觉子树创建** → **旧引用悬空** → **OnApplyTemplate 再次调用**
- **修复**:顶部先 `RemoveHandler` + 清字段 + 重新拿引用
- **`LogicalTreeHelper.GetParent(模板元素)` 返回 null 根因**:
- 模板内部实例化的元素(`PART_*`)**只属于视觉树**,不进逻辑树
- `LogicalTreeHelper` 沿逻辑树向上找父 → 模板元素不在逻辑树 → 返回 null
- **正确做法**:`VisualTreeHelper.GetParent` 拿视觉父级
- **`VisualBrush` 双刃剑**:
- ✅ 便利:把任意 `Visual` 当画刷用(缩略图 / 反射效果 / 实时预览)
- ❌ 风险:`new VisualBrush(this)` 强制 WPF **持续实时渲染**源 Visual → 高频更新场景 CPU 80%+
- **生产修复**:`RenderTargetBitmap.Render(this)` 一次性截图 → `ImageBrush` 零持续开销
- **`ChartCard` 完整生产代码核心点**:
- 静态 `DefaultStyleKeyProperty.OverrideMetadata` 指定默认模板(Control 基类不知道)
- `Loaded += OnLoadedOnce` 一次性访问 `VisualTreeHelper.GetChild(_plotArea!, 0)` 拿 `ItemsControl`
- `_itemsControl.AddHandler(MouseMove, ..., handledEventsToo: true)` 即使子元素 Handled 也能收到
- `_itemsControl.InputHitTest(pos) as Ellipse` + `Ellipse.Tag` 绑业务数据
- `PointToScreen`/`PointFromScreen` tooltip 跨窗口定位
- `OnMouseLeftButtonUp` + `e.OriginalSource as Ellipse` + `e.Handled=true` 阻止冒泡拖动
- **5 大生产陷阱**:
- **T1:VisualBrush 强制实时渲染**(CPU 80%+)→ 改 `RenderTargetBitmap` 一次性
- **T2:OnApplyTemplate 缓存悬空引用**(主题切换崩)→ 顶部 `RemoveHandler` + 清字段
- **T3:VirtualizingStackPanel 命中失效**(屏幕外元素未实例化)→ 数据点 < 500 用 `StackPanel` / > 500 用 `Canvas` / > 10000 用 LOD
- **T4:DataContext 沿逻辑树污染模板元素** → 显式 `DataContext="{x:Null}"`
- **T5:路由事件 Handled 提前阻止命中** → 父级 `AddHandler(..., handledEventsToo: true)` 仍能接收
- **关联知识网络**:
- **Day 14 WPF 架构** → `Visual` 是 `WindowsBase` + `PresentationCore` 层核心抽象
- **Day 15 DP** → 视觉树遍历找祖先 DP 时,`Inherits=true` 的 DP 沿视觉树向下传(如 `FontSize`)
- **Day 16 路由事件** → 事件沿视觉树传播是核心机制,`FindAncestor` + `OriginalSource` 配合精确定位
- **未讲 Phase 4 数据绑定** → `Binding` 沿逻辑树解析 `DataContext`,与视觉树路径完全不同
- **未讲 Phase 4 模板** → `ControlTemplate` 视觉子树是 OnApplyTemplate 展开,`DataTemplate` 是 ItemsControl 容器展开
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
布局系统(Measure / Arrange 两遍布局 / `MeasureOverride` + `ArrangeOverride` / `Panel` 派生 / `Arrange` vs `Render` / `InvalidateMeasure` vs `InvalidateArrange`)—— Day 18
---
## 📊 Day 18 教学记录(详细)
### 今日主题
WPF 布局系统(Layout System)核心(Measure/Arrange 两遍机制 / 4 个核心类 / 11 步管线 / DesiredSize vs RenderSize / InvalidateMeasure vs InvalidateArrange / WrapPanel 完整生产代码 + 5 大陷阱 + 3 个扩展思考)
### 教学重点
- **核心抽象**:WPF 布局系统 = Measure(父问子"你想要多大")+ Arrange(父给子"你最终多大")两遍机制,由 UIElement / FrameworkElement / Panel / Visual 4 个核心类协作完成
- **11 步布局管线**:child.Measure → MeasureCore → MeasureOverride → DesiredSize → 父收集 → child.Arrange → ArrangeCore → ArrangeOverride → 渲染 → RenderSize → OnRender。`MeasureCore` / `ArrangeCore` 是 sealed,派生类只能重写 MeasureOverride / ArrangeOverride
- **4 大铁律**:Measure 在 Arrange 前完成整棵子树 / Arrange 时 DesiredSize 已定可参考 / Measure 多次 Arrange 一次 / `RenderSize ≠ DesiredSize`(Arrange 可强制小于触发裁剪)
- **DesiredSize vs RenderSize 5 维对比**:含义(最小需求 vs 最终结果)/ 时机(Measure 后 vs Arrange 后)/ 决定者(子 vs 父)/ 作用(Arrange 矩形输入 vs Render 区域)/ 异常(不可能 vs 可裁剪)
- **InvalidateMeasure vs InvalidateArrange 6 维对比**:触发条件(DesiredSize 变 vs RenderSize 变但 DesiredSize 不变)/ 重跑范围(M+A 全树 vs 只 A)/ 性能(重 vs 轻)/ 场景(Text 变 vs 对齐变)/ API(InvalidateMeasure() vs InvalidateArrange())/ 实际触发(UpdateLayout() vs 只排)
- **WPF 6 大内置 Panel 策略表**:StackPanel(单向累加)/ WrapPanel(流式换行)/ DockPanel(剩余空间分配)/ Grid(行列切片)/ UniformGrid(等分 N×M)/ Canvas(不测量 + 显式坐标)。⚠️ Canvas 是唯一不测量的 Panel,子元素 DesiredSize 完全由自己决定
- **DP AffectsMeasure / AffectsArrange API**:Width/Height/Margin AffectsMeasure,HorizontalAlignment/VerticalAlignment AffectsArrange,Visibility=Visible→Collapsed AffectsMeasure,Visible→Hidden AffectsArrange
- **6 大误区**:T1 MeasureOverride 内改子属性栈溢出 / T2 ArrangeOverride 调 DesiredSize 当最终大小(子未 Measure → 0×0)/ T3 忘 base.MeasureOverride(Panel 不需要,Control 需要)/ T4 DesiredSize==RenderSize 误解 / T5 OnRender 内测量死循环 / T6 不重写 ArrangeOverride 子元素 0,0 堆叠
- **Q3 WrapPanel 完整 300 行生产代码**:Orientation DP FrameworkPropertyMetadataOptions.AffectsMeasure / MeasureOverride 传 Infinity 让子元素自决 / Margin.Left+Right+Top+Bottom 完整处理 / 换行算法 lineSize 累积 + 超出换行 + 提交最后一行 / 4 个场景分析:Margin 完整累加 / Orientation isHorizontal 分支 / 缓存不缓存(MeasureOverride 本质无状态)/ 虚拟化需继承 VirtualizingPanel + 重写 OnItemsChanged
- **5 大生产陷阱 T1-T5**:T1 改子属性栈溢出 → 只读 DesiredSize / T2 DP 缺 AffectsMeasure UI 不刷 → FrameworkPropertyMetadataOptions.AffectsMeasure / T3 ArrangeOverride 内 Measure 重布局 → Measure 在 MeasureOverride 内 / T4 VirtualizingPanel 漏 OnItemsChanged → 重写 + InvalidateMeasure / T5 WrapPanel 类 Grid 列宽无限大 → 显式 Width 或 HorizontalAlignment=Left
- **3 个扩展思考**:FlexPanel vs WrapPanel 性能(O(N×K) vs O(N))/ 浮点精度换行阈值(> availableSize.Width + 0.5 容忍 0.5px)/ DPI 125% 设备无关像素(1 DIP = 1.25 设备像素)
- **关联知识网络**:Day 14 WPF 架构(Panel 在 PresentationFramework)/ Day 15 DP(Width/Height/Margin/AffectsMeasure)/ Day 16 路由事件(LayoutUpdated 是路由事件)/ Day 17 视觉树(Measure/Arrange 沿视觉树遍历)/ 未讲 Phase 4 动画(Storyboard 改 DP 触发 InvalidateMeasure)/ 未讲 Phase 4 绑定(Binding 改 DP 触发重布局)/ 未讲 Phase 4 自定义控件(重写 MeasureOverride 必须 base)/ 未讲 Phase 4 渲染优化(DrawingVisual + VisualHost 绕过布局系统)
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
资源系统(StaticResource / DynamicResource / ResourceDictionary / MergedDictionaries / 资源查找层级 + FindResource vs TryFindResource / 隐式样式 Style 无 Key 自动应用)—— Day 19
---
## 📊 Day 22 教学记录(详细)
### 今日主题
WPF 样式与模板系统(Style 4 大核心 / ControlTemplate 5 大机制 / DataTemplate 5 大机制 / ItemsPanelTemplate / TemplateBinding 编译期优化 10-50x / 4 大触发器 / Style Trigger vs VSM / ValidationIndicator 完整生产实战 + 5 大生产陷阱 + 7 大易错点 + 3 个动手扩展思考 + 8 道自检题)
### 教学重点
- **核心 4 大革命**:WinForms 做不到的主题化 / 控件外观完全重定义 / 数据-视图分离 / 多状态自动响应 → WPF Style + ControlTemplate + DataTemplate + 触发器全解决
- **Style 4 大核心属性**:TargetType(必需,Setter 只接受 TargetType 上的属性,Day 15 DP 关联)/ BasedOn(链式继承)/ Resources(私有字典避免全局污染)/ Setters(含 Style.Triggers)
- **Style 选择器层级**:隐式样式(无 x:Key,按 TargetType 自动应用所有实例)/ 显式样式(有 x:Key,需手动 StaticResource 引用)/ 元素局部样式(直接 Style="...")→ 优先级铁律(局部 > 显式 > 隐式 > 主题默认)
- **ControlTemplate 5 大机制**:PART_* 命名约定(Code 与 Template 的契约,Day 17 关键点)/ TemplateBinding 编译期优化(直接订阅 PropertyChanged 跳过 BindingExpression 反射链,性能 10-50x)/ ContentPresenter 自动应用(ContentSource 属性指定显示哪个 DP)/ 触发器与状态(ControlTemplate.Triggers 旧式 + VisualStateManager VSM 新式推荐)/ OverridesDefaultStyle(完全跳过主题默认模板)
- **DataTemplate 5 大机制**:DataType 自动选择(按运行时类型自动套模板,比 Style.TargetType 更精细)/ DataTemplateSelector 运行时选择(根据数据属性选不同模板)/ HierarchicalDataTemplate 树形递归(TreeView 专用,HeaderedItemsControl 用 ItemsPresenter 而非 ContentPresenter)/ ContentPresenter 自动应用(5 大隐式应用场景)/ ItemTemplate 列表项模板(ListBox/ComboBox 等 ItemsControl)
- **ItemsPanelTemplate 4 大实战场景**:WrapPanel 标签云流式布局 / Canvas 画板自由定位 / UniformGrid 启动器等分 / VirtualizingStackPanel 大数据虚拟化
- **TemplateBinding 编译期优化根因**:XAML 编译期创建轻量 TemplateBindingExpression,直接订阅 TemplatedParent 的 PropertyChanged 并 SetValue 到目标元素;vs `{Binding RelativeSource=TemplatedParent}` 走完整 BindingExpression 管线(反射解析 Path + 订阅 + UpdateTarget + SetValue),实测 1000 次模板创建 5ms vs 250ms = **50 倍性能差**
- **TemplateBinding 5 大限制**:单向(不能 TwoWay)/ DataTemplate 内不可用(要 DataContext)/ 无 IValueConverter(需转换走 Binding)/ 不能在 Style Trigger 内(Trigger Setter 只能用 StaticResource)/ 不暴露 BindingExpression 事件(拦截不了 TargetUpdated)
- **4 大触发器完整对比**:Trigger 单 DP 属性 / MultiTrigger 多 DP 属性 AND / DataTrigger 绑定数据值(要求 INPC)/ MultiDataTrigger 多绑定数据 AND / EventTrigger 路由事件启动 Storyboard
- **Style Trigger vs VSM 6 维对比**:出现版本(3.0 vs 4.0)/ 状态模型(离散条件 vs 状态组)/ 过渡动画(瞬间切换 vs Storyboard Transition)/ 自定义控件模板(简单场景 vs 复杂控件 Modern WPF)/ 触摸键盘焦点(不感知 vs 状态组全覆盖)
- **关联知识网络**:
- Day 15 DP → Style Setter 必须是 TargetType 上的 DP,TemplateBinding 同理
- Day 17 视觉树 → ControlTemplate 内的所有元素都是视觉子树成员,OnApplyTemplate 才能拿 PART_*
- Day 18 布局 → DataTemplate 内的布局走完整 Measure/Arrange 管线
- Day 19 资源系统 → Style/DataTemplate 都定义在 ResourceDictionary 里,隐式样式走合并字典后覆盖前
- Day 20 数据绑定 → TemplateBinding 是 Binding 的子集(编译期优化版)
- Day 21 命令系统 → ControlTemplate 内的 Button.Command 通过 TemplateBinding 自动连到父级
- **Q1 完整方案**:4 大核心职责 + 完整协作关系图(Style → Setter Template→ControlTemplate + Setter ContentTemplate/ItemTemplate→DataTemplate + ItemsPanel→ItemsPanelTemplate)+ 5 大隐式应用场景 + 4 大实战场景
- **Q2 完整方案**:TemplateBinding vs Binding RelativeSource=TemplatedParent 6 维对比表 + TemplateBindingExpression 伪代码 + 性能实测 50x + 5 大限制 + HierarchicalDataTemplate 递归原理 + 4 种循环引用防御方案 + 5 易错点
- **Q3 完整 ValidationIndicator 生产代码**:静态 DefaultStyleKeyProperty.OverrideMetadata + 3 个 PART_* 命名常量 + OnApplyTemplate 顶部清理旧引用防 T2 主题切换 + IsValid DP FrameworkPropertyMetadataOptions.AffectsRender + Tooltip 动态更新 + Themes/Generic.xaml 4 状态 DataTrigger 用 RelativeSource Self + x:Static 引用枚举值 + TargetName 限定 Setter + 5 大生产陷阱完整错误示例与修复 T1-T5 + 7 大易错点 + 3 个动手实践 + 8 道自检题
### 主人反馈
- _等待主人回复_
### 主人答案评估
- _等待主人回复_
### 下次起点
自定义控件开发(Generic.xaml 全套主题 / 自定义控件 vs 用户控件区别 / 控件继承链选择 / 自定义路由事件 / 自定义 DP / OnRender 绘制 vs VisualHost / 完整 ToggleSwitch 开关控件生产实战 + 5 大生产陷阱)—— Day 23
---
_严老师专用 · 主人 + 小 Q 共同维护 · 2026-08-22 v1.22(Day 22 推送完成,Phase 3 第 9 课,等待主人回复)_