请说明在 C# 中,如何实现一个能够在单个元素发生更改时发出通知的集合?
考察说明
考查 C# 中自定义集合与通知机制的实现能力,以及 ObservableCollection 的局限性和解决方案。
回答思路
- 【回答框架 1】标准方案是继承 ObservableCollection<T>,它实现了 INotifyCollectionChanged 和 INotifyPropertyChanged,当集合添加、移除或重置时会触发 CollectionChanged 事件,但不会监听集合内元素的属性变化。若需监听元素更改,通常要求元素实现 INotifyPropertyChanged,并在集合中订阅每个元素的 PropertyChanged 事件,在事件处理器中将其转发为集合的 CollectionChanged 或自定义事件。
- 【回答框架 2】实现细节:在重写 InsertItem、RemoveItem、ClearItems 等方法时,为新元素订阅 PropertyChanged,为移除的元素取消订阅。这样能避免内存泄漏。同时可定义 SetItem 或 ReplaceItem 来处理元素替换,确保旧元素取消订阅、新元素订阅。
- 【回答框架 3】另一种方案是使用 BindingList<T>,它支持 ListChanged 事件,但只覆盖列表的整体变化,对元素属性变化需额外处理。也可考虑使用社区库如 DynamicData,但核心原理类似。
- 【回答框架 4】选择时需权衡:若只需监听集合结构的增减,ObservableCollection 直接可用;若需监听元素属性变化,则必须自定义包装器,并注意线程安全(如使用锁或同步上下文)。性能上,大量元素时频繁订阅/取消订阅可能带来开销,需考虑事件处理器生命周期管理。
- 【关键点 1】ObservableCollection 只通知集合结构变化,不通知元素自身变化。
- 【关键点 2】自定义集合需实现 INotifyCollectionChanged 并订阅元素 INotifyPropertyChanged。
- 【关键点 3】正确管理事件订阅,防止内存泄漏。
- 【关键点 4】元素类型必须实现 INotifyPropertyChanged 才能感知其属性变更。
- 【易错点 1】未在元素移除时取消事件订阅,导致内存泄漏。
- 【易错点 2】忽略线程安全,并发修改可能导致事件顺序异常。
- 【易错点 3】误以为 ObservableCollection 会自动监听元素属性变化,实际不会。