我有一个自定义的 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/