How to increase the efficiency of the AddRange method in a custom BindingList?

I have a custom BindingList for which I want to create my own AddRange method for.

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

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

My problem with this is that performance is terrible with large collections. The case that I am debugging now is trying to add about 30,000 entries and take an invalid amount of time.

After exploring this problem on the Internet, it seems that the problem is that use is Addresizing the array with each addition. This answer I believe is:

If you use Add, it gradually resizes the internal array (doubles)

AddRange, , BindingList, , ?

+7
4

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> PropertyChanged. , Reflection, HookPropertyChanged AddRange. , vals ( ) . .

, "", BindingList. , BindingList .

+6

CSharpie , ListChanged -event Add AddRange BindingList.

AddRange BindingList<T>. CSharpies:

/// <summary>
/// Extension methods for <see cref="System.ComponentModel.BindingList{T}"/>.
/// </summary>
public static class BindingListExtensions
{
  /// <summary>
  /// Adds the elements of the specified collection to the end of the <see cref="System.ComponentModel.BindingList{T}"/>,
  /// while only firing the <see cref="System.ComponentModel.BindingList{T}.ListChanged"/>-event once.
  /// </summary>
  /// <typeparam name="T">
  /// The type T of the values of the <see cref="System.ComponentModel.BindingList{T}"/>.
  /// </typeparam>
  /// <param name="bindingList">
  /// The <see cref="System.ComponentModel.BindingList{T}"/> to which the values shall be added.
  /// </param>
  /// <param name="collection">
  /// The collection whose elements should be added to the end of the <see cref="System.ComponentModel.BindingList{T}"/>.
  /// The collection itself cannot be null, but it can contain elements that are null,
  /// if type T is a reference type.
  /// </param>
  /// <exception cref="ArgumentNullException">values is null.</exception>
  public static void AddRange<T>(this System.ComponentModel.BindingList<T> bindingList, IEnumerable<T> collection)
  {
    // The given collection may not be null.
    if (collection == null)
      throw new ArgumentNullException(nameof(collection));

    // Remember the current setting for RaiseListChangedEvents
    // (if it was already deactivated, we shouldn't activate it after adding!).
    var oldRaiseEventsValue = bindingList.RaiseListChangedEvents;

    // Try adding all of the elements to the binding list.
    try
    {
      bindingList.RaiseListChangedEvents = false;

      foreach (var value in collection)
        bindingList.Add(value);
    }

    // Restore the old setting for RaiseListChangedEvents (even if there was an exception),
    // and fire the ListChanged-event once (if RaiseListChangedEvents is activated).
    finally
    {
      bindingList.RaiseListChangedEvents = oldRaiseEventsValue;

      if (bindingList.RaiseListChangedEvents)
        bindingList.ResetBindings();
    }
  }
}

, BindingList -subclass.

+4

, , , . CSharpie, .

  • just replacing "bindingList.Add (value);" statement with "bindingList.Remove (value);"

How...

public static void DeleteRange<UnitObject>(this System.ComponentModel.BindingList<UnitObject> bindingList, IEnumerable<UnitObject> collection)
    {
        // The given collection may not be null.
        if (collection == null)
            throw new ArgumentNullException(nameof(collection));

        // Remember the current setting for RaiseListChangedEvents
        // (if it was already deactivated, we shouldn't activate it after adding!).
        var oldRaiseEventsValue = bindingList.RaiseListChangedEvents;

        // Try deleting all of the elements to the binding list.
        try
        {
            bindingList.RaiseListChangedEvents = false;

            foreach (var value in collection)
                bindingList.Remove(value);
        }

        // Restore the old setting for RaiseListChangedEvents (even if there was an exception),
        // and fire the ListChanged-event once (if RaiseListChangedEvents is activated).
        finally
        {
            bindingList.RaiseListChangedEvents = oldRaiseEventsValue;

            if (bindingList.RaiseListChangedEvents)
                bindingList.ResetBindings();
        }
    }
0
source

You can minimize calls using the AddRange list method when the source collection is not empty.

how

public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
        {
            var tempList = list as List<T>;
            if (tempList != null)
            {
                tempList.AddRange(items);
            }
            else
            {
                foreach (var item in items)
                {
                    list.Add(item);
                }
            }
        }

But if your initial collection is zero, you should use the same approach. I hope for this help.

-2
source

Source: https://habr.com/ru/post/1674511/


All Articles