我有一个自定义的 BindingList,我想为其创建一个自定义的 AddRange 方法。

public class MyBindingList<I> : BindingList<I>
{
    ...

    public void AddRange(IEnumerable<I> vals)
    {
        foreach (I v in vals)
            Add(v);
    }
}

我的问题是大型集合的性能很糟糕。我现在正在调试的案例试图添加大约 30,000 条记录,并且花费了无法接受的时间。

在线查看此问题后,似乎问题在于使用 Add 时每次添加都会调整数组的大小。 This answer 我认为将其总结为:



我可以在我的自定义 AddRange 实现中做什么来指定 BindingList 需要根据项目计数调整大小,而不是让它不断地重新分配数组并添加每个项目?

最佳答案

您可以在构造函数中传入一个 List 并使用 List<T>.Capacity

但我敢打赌,最显着的加速将来自添加范围时暂停事件。所以我在我的示例代码中包含了这两件事。

可能需要一些微调来处理一些最坏的情况,什么不是。

public class MyBindingList<I> : BindingList<I>
{
    private readonly List<I> _baseList;

    public MyBindingList() : this(new List<I>())
    {

    }

    public MyBindingList(List<I> baseList) : base(baseList)
    {
        if(baseList == null)
            throw new ArgumentNullException();
        _baseList = baseList;
    }

    public void AddRange(IEnumerable<I> vals)
    {
        ICollection<I> collection = vals as ICollection<I>;
        if (collection != null)
        {
            int requiredCapacity = Count + collection.Count;
            if (requiredCapacity > _baseList.Capacity)
                _baseList.Capacity = requiredCapacity;
        }

        bool restore = RaiseListChangedEvents;
        try
        {
            RaiseListChangedEvents = false;
            foreach (I v in vals)
                Add(v); // We cant call _baseList.Add, otherwise Events wont get hooked.
        }
        finally
        {
            RaiseListChangedEvents = restore;
            if (RaiseListChangedEvents)
                ResetBindings();
        }
    }
}

您不能使用 _baseList.AddRange,因为 BindingList<T> 那时不会 Hook PropertyChanged 事件。您可以通过在 AddRange 之后为每个 Item 调用私有(private)方法 HookPropertyChanged 来仅使用反射来绕过此问题。然而,这仅在 vals (您的方法参数)是一个集合时才有意义。否则,您可能会面临两次枚举可枚举的风险。

这是您在不编写自己的 BindingList 的情况下最接近“最佳”的方法。
这不应该太难,因为您可以从 BindingList 复制源代码并根据需要更改部分。

关于c# - 如何提高自定义 BindingList 上的 AddRange 方法的性能?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43331145/

10-17 02:15