You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

泛型基类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;
        }
    }
}

关键改进点说明

  1. 移除冗余接口:删除了IEnumerable和ICollection的显式继承,避免接口实现冲突,同时保持原有功能。
  2. 真正的懒加载触发:通过EnsureInitialized()方法,在首次访问Count、枚举集合、修改集合或访问索引器时才执行工厂方法初始化元素,完全符合懒加载的设计目标。
  3. 线程安全保障:使用双重检查锁定(double-checked locking)机制,确保多线程环境下工厂方法只会被执行一次,避免重复初始化和数据竞争。
  4. 完善的触发时机:覆盖了所有可能触发集合访问的场景,确保无论用户以何种方式操作集合,都会先完成初始化。

使用方式(和你原来的调用一致)

var lazyCollection = new LazyObservableCollection<string>(x => {
    x.Add("Item1");
    x.Add("Item2");
});

// 此时工厂方法还未执行,直到第一次访问集合:
Console.WriteLine(lazyCollection.Count); // 这里才会触发初始化,输出2

内容的提问来源于stack exchange,提问作者SledgeHammer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:54:20