增强型集合ObservableCollectionEx的使用与性能分析

.NET框架中,数据绑定是一个常见的需求,而ObservableCollection是实现数据绑定的常用集合类型。它能够在集合发生变化时,如添加、移除或刷新时,自动通知绑定的UI元素进行更新。然而,在某些情况下,可能需要延迟或暂时禁用这些通知,例如在批量更新数据时,以提高性能或避免界面闪烁。遗憾的是,标准的ObservableCollection并没有提供这样的功能。

为了解决这个问题,ObservableCollectionEx应运而生。它旨在提供缺失的功能,即延迟或禁用通知,同时完全兼容ObservableCollection的使用方式。

为了实现通知的延迟,需要暂时将通知重定向到一个存储位置,并在不再需要延迟时一次性触发它们。同时,还需要为不需要延迟通知的集合消费者提供正常的功能和通知。这可以通过提供多个包装同一集合的外壳来实现。一个外壳实例包含元素容器并托管所有通知事件,而其他外壳实例则处理禁用和延迟的事件。这些额外的外壳引用相同的容器,但它们不会触发变更事件,而是收集这些事件,并在外壳释放时一次性触发它们。

实现原理

ObservableCollection的实现基于一个Collection类,该类实现了ICollection的操作和ObservableCollection的实现通知。Collection类作为IList接口的外壳实现,包含对容器的引用,并通过它操作容器。Collection类的一个构造函数接受List作为参数,允许这个列表成为该Collection的容器。这为提供了多个Collection实例来操作同一个容器,这正是所需要的。

不幸的是,这种能力在ObservableCollection的实现中丢失了。它不是将IList分配给实例作为容器,而是创建了那个List的副本,并使用该副本存储元素。这个限制阻止了从ObservableCollection类继承。

ObservableCollectionEx基于Collection类(与ObservableCollection相同),并实现了与ObservableCollection完全相同的方法和属性。除了这些成员之外,ObservableCollectionEx还暴露了两个方法来创建禁用或延迟通知的外壳。

使用方法

ObservableCollectionEx类包含到项目中最简单的方法是通过安装Nuget包。它应该像ObservableCollection一样使用。它可以实例化并用作ObservableCollection的替代品,或者从中派生。不需要特殊处理。

为了延迟通知,建议使用using指令:

ObservableCollectionEx target = new ObservableCollectionEx(); using (ObservableCollectionEx iDelayed = target.DelayNotifications()) { iDelayed.Add(item0); iDelayed.Add(item0); iDelayed.Add(item0); }

由于通知参数的设计,不可能将不同的操作组合在一起。例如,不可能在同一个延迟实例上同时添加和移除元素,除非在这些调用之间调用了Dispose()。调用Dispose()将触发之前收集的事件,并重新初始化操作。

性能分析

一般来说,ObservableCollectionObservableCollectionEx提供可比较的性能。测试使用了包含10,000个唯一对象的数组。两者都使用这个数组初始化以预分配存储空间,因此不影响计时结果。应用程序运行了大约一打次,以让JIT优化可执行文件,然后收集测试结果。

测试包括10,000次添加、替换和移除操作。使用Stopwatch类收集计时,并以毫秒为单位呈现。

从图表中可以看出,禁用通知的接口性能与订阅者数量无关。由于一些性能增强,ObservableCollectionEx无论订阅者数量如何,都比ObservableCollection稍微表现得更好,但显然一旦有多个订阅者,它就失去了禁用接口。

当通知被延迟时,ObservableCollectionEx的性能与上述结果不同。由于通知只调用一次,它节省了一些时间,但它需要一些额外的处理来展开保存的通知。ObservableCollectionObservableCollectionEx的通知时间由以下方程式描述:

ObservableCollection: overhead = (n * a) + (n * b) ObservableCollectionEx: overhead = a + c + (n * b)

其中a是执行通知所需的恒定开销,n是变更元素的数量,b是重绘每个单独元素的成本,c是执行延迟通知所需的开销。

在这些方程式中,ac是常数,因此性能仅取决于两个元素:b——重绘每个元素所需的时间,和n——通知的元素数量。如所知,b控制图表上升的陡度。因此,当重绘每个元素所需的时间(b)增加时,这两条线更早地相遇。这意味着看到性能优势所需的变更元素数量更少。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485