Descriptions 性能优化
状态:已完成 Gallery 实测、结构优化与生命周期验证;本轮追加 collection changed item indexed traversal。
追加结构优化:collection changed items indexed traversal
Descriptions.HandleCollectionChanged() 处理 Items 增删时,旧路径把 NotifyCollectionChangedEventArgs.NewItems/OldItems 先包装成 OfType<DescriptionItem>(),再交给 add/remove helper。本轮为 IList 通知列表增加直接 Count/indexer helper,保留原有 DescriptionItem 类型要求、布局生成和移除逻辑。
正确性补充:状态验证暴露了四个动态集合问题。第一,非 Window TopLevel / headless 场景下 _effectiveColumns 可能保持 0,动态 add 后 Grid.ColumnSpan=0 抛异常;本轮在 layout 前补齐 effective columns fallback,默认走 ExtraExtraLarge 断点。第二,动态 insert/remove 旧路径没有按 NewStartingIndex/OldStartingIndex 操作 generated children,插入到中间或移除已删除项时可能让 visual children 和 Items 顺序错位;本轮按 item index 映射到 grid child index。第三,Items.Clear() 旧路径抛 NotSupportedException,本轮 Reset 直接清空 generated children。第四,Items 属性替换后旧集合仍保留订阅、新集合未订阅;本轮替换时释放旧订阅、订阅新集合并重建 generated children。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
| Descriptions add item filter LINQ operators / add | 1 operator | 0 operators | (1 - 0) / 1 |
100.00% | 有效;新增项直接按 NewItems.Count/indexer 处理 |
| Descriptions remove item filter LINQ operators / remove | 1 operator | 0 operators | (1 - 0) / 1 |
100.00% | 有效;移除项直接按 OldItems.Count/indexer 处理 |
| Descriptions headless dynamic add column span correctness | may throw ColumnSpan=0 |
fallback columns before layout | correctness | 已修复 | 非 Window TopLevel 下动态 add 不再因 _effectiveColumns=0 崩溃 |
| Descriptions generated child index correctness / insert-remove | append / current-index remove | starting-index insert-remove | correctness | 已修复 | 中间插入和移除按通知 index 更新 generated children |
| Descriptions reset collection behavior / clear | throws NotSupportedException |
clears generated children | correctness | 已修复 | Items.Clear() 后 generated visuals 清空 |
| Descriptions Items replacement subscription lifecycle | old collection still subscribed | old unsubscribed / new subscribed | correctness | 已修复 | 替换 Items 后新集合驱动 UI,旧集合 mutation 不再影响 UI |
说明:这是动态增删描述项的结构性收益;不声明页面导航 timing 提升。
追加结构优化:ItemsSource 导入去 LINQ
Descriptions.ItemsSource 变更时,旧路径使用 ItemsSource.OfType<DescriptionItem>() 后再 Items.AddRange()。本轮改为显式类型判断:IList 来源直接用 Count/indexer 收集,普通 IEnumerable 来源用 foreach 收集,然后仍然一次 Items.AddRange() 写入,保留批量通知语义。行为仍只导入 DescriptionItem,其他对象继续忽略。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
| ItemsSource import LINQ operators / source change | 1 operator | 0 operators | (1 - 0) / 1 |
100.00% | 有效;去掉 OfType<DescriptionItem>() |
| IList ItemsSource collection enumerators / source change | 1 enumerator | 0 enumerators | (1 - 0) / 1 |
100.00% | 有效;列表来源直接 Count/indexer 导入 |
| ItemsSource collection notifications / source change | 1 batch | 1 batch | (1 - 1) / 1 |
0.00% | 行为保持;仍然一次 AddRange(),不拆成逐项布局 |
| 非 DescriptionItem 过滤语义 | preserved | preserved | n/a | 0.00% | 行为保持;仍只接受 DescriptionItem |
说明:这是 ItemsSource 变更路径的结构性收益;未新增 Gallery timing 对比,不声明页面加载速度提升。
追加结构优化:只读 ItemsSource 预分配
Descriptions.ItemsSource 支持 IEnumerable。上一轮已让 IList 来源走 Count/indexer,本轮补齐 IReadOnlyList<DescriptionItem> 快路径:只读类型化列表按剩余 Count 作为上界预分配并通过 indexer 复制,避免 fallback foreach 路径从默认容量动态增长。空只读列表和全 null 列表仍返回 null,保持不触发 Items.AddRange() 的旧语义。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
| Descriptions ItemsSource list growth / read-only typed source import | dynamic growth | remaining-count upper-bound capacity | structural | 结构收益 | 只读类型化 source 按剩余 Count 预分配并保留 null 跳过语义 |
| Empty read-only ItemsSource import behavior | no AddRange | no AddRange | n/a | 0.00% | 行为保持 |
| Page-load timing claim | none | none | n/a | n/a | 本轮没有有效前后 timing,不声明页面级速度收益 |
追加结构优化:MediaBreakInfo 单值解析 span trim
DescriptionsMediaBreakInfo.Parse() 的单值 fast path 旧实现先 input.Trim() 再 int.TryParse()。本轮改为 input.AsSpan().Trim() 后直接解析,key-value 复杂格式仍走原有 span parser。
| 指标 | 优化前 | 优化后 | 公式 | 提升 | 结论 |
|---|---|---|---|---|---|
| Descriptions media break single-value trim temp strings / parse | 1 | 0 | (1 - 0) / 1 |
100.00% | 结构收益;单值解析语义保持 |
| Page-load timing claim | none | none | n/a | n/a | 本轮没有有效前后 timing,不声明页面级速度收益 |
验证补充:--verify-descriptions-states 当前在本轮改动和 clean HEAD d35b21a1a 上均失败于相同的 HeaderLayout / horizontal bordered item visual 断言,因此不是本轮 parser 优化引入的问题。