泛型基类LazyObservableCollection的Action工厂构造函数技术问题
解决LazyObservableCollection的懒加载实现问题
首先,咱们先梳理下当前实现存在的核心问题,再一步步给出修复方案:
现存问题分析
- 冗余接口继承:
ObservableCollection<T>本身已经实现了IEnumerable和ICollection接口,显式声明继承属于多余操作,反而可能引发接口实现冲突。 - 未真正实现懒加载:当前构造函数在实例化对象时就立刻执行了工厂方法添加项,这和"懒加载"(首次访问时才初始化)的设计目标不符。
- 无初始化状态控制:缺少标记是否已完成初始化的机制,可能导致工厂方法被重复调用,造成重复添加元素的问题。
- 线程安全隐患:多线程场景下,可能出现多个线程同时触发初始化的情况,引发数据不一致。
修复后的完整实现
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading; public class LazyObservableCollection<T> : ObservableCollection<T> { private readonly Action<LazyObservableCollection<T>> _itemsFactory; private bool _isInitialized; private readonly object _lockObj = new object(); // 构造函数仅保存工厂委托,不立即执行 public LazyObservableCollection(Action<LazyObservableCollection<T>> itemsFactory) { _itemsFactory = itemsFactory ?? throw new ArgumentNullException(nameof(itemsFactory)); } // 核心初始化方法,带双重检查锁定确保线程安全且仅执行一次 private void EnsureInitialized() { if (!_isInitialized) { lock (_lockObj) { if (!_isInitialized) { _itemsFactory(this); _isInitialized = true; } } } } // 重写Count属性,访问前先确保初始化 protected override int Count { get { EnsureInitialized(); return base.Count; } } // 重写枚举器,枚举前先初始化 public override IEnumerator<T> GetEnumerator() { EnsureInitialized(); return base.GetEnumerator(); } // 重写非泛型枚举器 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { EnsureInitialized(); return base.GetEnumerator(); } // 重写修改集合的方法,操作前先初始化(避免未初始化时修改导致的异常) protected override void InsertItem(int index, T item) { EnsureInitialized(); base.InsertItem(index, item); } protected override void RemoveItem(int index) { EnsureInitialized(); base.RemoveItem(index); } protected override void SetItem(int index, T item) { EnsureInitialized(); base.SetItem(index, item); } protected override void ClearItems() { EnsureInitialized(); base.ClearItems(); } // 重写索引器,访问前初始化 public override T this[int index] { get { EnsureInitialized(); return base[index]; } set { EnsureInitialized(); base[index] = value; } } }
关键改进点说明
- 移除冗余接口:删除了
IEnumerable和ICollection的显式继承,避免接口实现冲突,同时保持原有功能。 - 真正的懒加载触发:通过
EnsureInitialized()方法,在首次访问Count、枚举集合、修改集合或访问索引器时才执行工厂方法初始化元素,完全符合懒加载的设计目标。 - 线程安全保障:使用双重检查锁定(double-checked locking)机制,确保多线程环境下工厂方法只会被执行一次,避免重复初始化和数据竞争。
- 完善的触发时机:覆盖了所有可能触发集合访问的场景,确保无论用户以何种方式操作集合,都会先完成初始化。
使用方式(和你原来的调用一致)
var lazyCollection = new LazyObservableCollection<string>(x => { x.Add("Item1"); x.Add("Item2"); }); // 此时工厂方法还未执行,直到第一次访问集合: Console.WriteLine(lazyCollection.Count); // 这里才会触发初始化,输出2
内容的提问来源于stack exchange,提问作者SledgeHammer
相关产品推荐
相关产品推荐

