栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

ObservableCollection不支持AddRange方法,因此除了INotifyCollectionChanging之外,我还会收到添加项的通知。

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ObservableCollection不支持AddRange方法,因此除了INotifyCollectionChanging之外,我还会收到添加项的通知。

首先,请对.NET存储库中的API请求进行
投票和评论


这里是我的优化的版本

ObservableRangeCollection
(优化詹姆斯Montemagno的版本一个)。

它执行速度非常快,旨在在可能的情况下重用现有元素,避免不必要的事件,或者在可能的情况下将它们成批处理。该

ReplaceRange
方法通过适当的索引替换/删除/添加所需的元素,并分批处理可能的事件。

在Xamarin.Forms UI上进行了测试,结果非常好,可以非常频繁地对大型集合进行更新(每秒5-7次更新)。

注意:由于 WPF
不习惯使用范围操作,因此在WPF与UI相关的工作中

NotSupportedException
使用
ObservableRangeCollection
from
时(例如将其绑定到
ListBox
等),它将抛出,(您仍可以使用
ObservableRangeCollection<T>
if,如果未绑定到UI) 。
但是,您可以使用
WpfObservableRangeCollection<T>
解决方法。
真正的解决方案是创建一个
CollectionView
知道如何处理范围运算的,但是我仍然没有时间实现它。

RAW代码
-以Raw
格式
打开,然后执行

Ctrl
+
A
选择全部,然后执行
Ctrl
+
C
复制。

// Licensed to the .NET Foundation under one or more agreements.// The .NET Foundation licenses this file to you under the MIT license.// See the LICENSE file in the project root for more information.using System.Collections.Generic;using System.Collections.Specialized;using System.ComponentModel;using System.Diagnostics;namespace System.Collections.ObjectModel{  /// <summary>  /// Implementation of a dynamic data collection based on generic Collection&lt;T&gt;,  /// implementing INotifyCollectionChanged to notify listeners  /// when items get added, removed or the whole list is refreshed.  /// </summary>  public class ObservableRangeCollection<T> : ObservableCollection<T>  {    //------------------------------------------------------    //    //  Private Fields    //    //------------------------------------------------------    #region Private Fields        [NonSerialized]    private DeferredEventsCollection _deferredEvents;    #endregion Private Fields    //------------------------------------------------------    //    //  Constructors    //    //------------------------------------------------------    #region Constructors    /// <summary>    /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity.    /// </summary>    public ObservableRangeCollection() { }    /// <summary>    /// Initializes a new instance of the ObservableCollection class that contains    /// elements copied from the specified collection and has sufficient capacity    /// to accommodate the number of elements copied.    /// </summary>    /// <param name="collection">The collection whose elements are copied to the new list.</param>    /// <remarks>    /// The elements are copied onto the ObservableCollection in the    /// same order they are read by the enumerator of the collection.    /// </remarks>    /// <exception cref="ArgumentNullException"> collection is a null reference </exception>    public ObservableRangeCollection(IEnumerable<T> collection) : base(collection) { }    /// <summary>    /// Initializes a new instance of the ObservableCollection class    /// that contains elements copied from the specified list    /// </summary>    /// <param name="list">The list whose elements are copied to the new list.</param>    /// <remarks>    /// The elements are copied onto the ObservableCollection in the    /// same order they are read by the enumerator of the list.    /// </remarks>    /// <exception cref="ArgumentNullException"> list is a null reference </exception>    public ObservableRangeCollection(List<T> list) : base(list) { }    #endregion Constructors    //------------------------------------------------------    //    //  Public Methods    //    //------------------------------------------------------    #region Public Methods    /// <summary>    /// Adds the elements of the specified collection to the end of the <see cref="ObservableCollection{T}"/>.    /// </summary>    /// <param name="collection">    /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{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"><paramref name="collection"/> is null.</exception>    public void AddRange(IEnumerable<T> collection)    {      InsertRange(Count, collection);    }    /// <summary>    /// Inserts the elements of a collection into the <see cref="ObservableCollection{T}"/> at the specified index.    /// </summary>    /// <param name="index">The zero-based index at which the new elements should be inserted.</param>    /// <param name="collection">The collection whose elements should be inserted into the List<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"><paramref name="collection"/> is null.</exception>    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is not in the collection range.</exception>    public void InsertRange(int index, IEnumerable<T> collection)    {      if (collection == null)        throw new ArgumentNullException(nameof(collection));      if (index < 0)        throw new ArgumentOutOfRangeException(nameof(index));      if (index > Count)        throw new ArgumentOutOfRangeException(nameof(index));      if (collection is ICollection<T> countable)      {        if (countable.Count == 0)        {          return;        }      }      else if (!ContainsAny(collection))      {        return;      }      CheckReentrancy();      //expand the following couple of lines when adding more constructors.      var target = (List<T>)Items;      target.InsertRange(index, collection);      onEssentialPropertiesChanged();      if (!(collection is IList list))        list = new List<T>(collection);      onCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list, index));    }    /// <summary>     /// Removes the first occurence of each item in the specified collection from the <see cref="ObservableCollection{T}"/>.    /// </summary>    /// <param name="collection">The items to remove.</param> /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>    public void RemoveRange(IEnumerable<T> collection)    {      if (collection == null)        throw new ArgumentNullException(nameof(collection));      if (Count == 0)      {        return;      }      else if (collection is ICollection<T> countable)      {        if (countable.Count == 0)          return;        else if (countable.Count == 1)          using (IEnumerator<T> enumerator = countable.GetEnumerator())          { enumerator.MoveNext(); Remove(enumerator.Current); return;          }      }      else if (!(ContainsAny(collection)))      {        return;      }      CheckReentrancy();      var clusters = new Dictionary<int, List<T>>();      var lastIndex = -1;      List<T> lastCluster = null;      foreach (T item in collection)      {        var index = IndexOf(item);        if (index < 0)        {          continue;        }        Items.RemoveAt(index);        if (lastIndex == index && lastCluster != null)        {          lastCluster.Add(item);        }        else        {          clusters[lastIndex = index] = lastCluster = new List<T> { item };        }      }      onEssentialPropertiesChanged();      if (Count == 0)        onCollectionReset();      else        foreach (KeyValuePair<int, List<T>> cluster in clusters)          onCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster.Value, cluster.Key));    }    /// <summary>    /// Iterates over the collection and removes all items that satisfy the specified match.    /// </summary>    /// <remarks>The complexity is O(n).</remarks>    /// <param name="match"></param>    /// <returns>Returns the number of elements that where </returns>    /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>    public int RemoveAll(Predicate<T> match)    {      return RemoveAll(0, Count, match);    }    /// <summary>    /// Iterates over the specified range within the collection and removes all items that satisfy the specified match.    /// </summary>    /// <remarks>The complexity is O(n).</remarks>    /// <param name="index">The index of where to start performing the search.</param>    /// <param name="count">The number of items to iterate on.</param>    /// <param name="match"></param>    /// <returns>Returns the number of elements that where </returns>    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>    /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>    /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>    public int RemoveAll(int index, int count, Predicate<T> match)    {      if (index < 0)        throw new ArgumentOutOfRangeException(nameof(index));      if (count < 0)        throw new ArgumentOutOfRangeException(nameof(count));      if (index + count > Count)        throw new ArgumentOutOfRangeException(nameof(index));      if (match == null)        throw new ArgumentNullException(nameof(match));      if (Count == 0)        return 0;      List<T> cluster = null;      var clusterIndex = -1;      var removedCount = 0;      using (BlockReentrancy())      using (DeferEvents())      {        for (var i = 0; i < count; i++, index++)        {          T item = Items[index];          if (match(item))          { Items.RemoveAt(index); removedCount++; if (clusterIndex == index) {   Debug.Assert(cluster != null);   cluster.Add(item); } else {   cluster = new List<T> { item };   clusterIndex = index; } index--;          }          else if (clusterIndex > -1)          { onCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex)); clusterIndex = -1; cluster = null;          }        }        if (clusterIndex > -1)          onCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));      }      if (removedCount > 0)        onEssentialPropertiesChanged();      return removedCount;    }    /// <summary>    /// Removes a range of elements from the <see cref="ObservableCollection{T}"/>>.    /// </summary>    /// <param name="index">The zero-based starting index of the range of elements to remove.</param>    /// <param name="count">The number of elements to remove.</param>    /// <exception cref="ArgumentOutOfRangeException">The specified range is exceeding the collection.</exception>    public void RemoveRange(int index, int count)    {      if (index < 0)        throw new ArgumentOutOfRangeException(nameof(index));      if (count < 0)        throw new ArgumentOutOfRangeException(nameof(count));      if (index + count > Count)        throw new ArgumentOutOfRangeException(nameof(index));      if (count == 0)        return;      if (count == 1)      {        RemoveItem(index);        return;      }      //Items will always be List<T>, see constructors      var items = (List<T>)Items;      List<T> removedItems = items.GetRange(index, count);      CheckReentrancy();      items.RemoveRange(index, count);      onEssentialPropertiesChanged();      if (Count == 0)        onCollectionReset();      else        onCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, index));    }    /// <summary>     /// Clears the current collection and replaces it with the specified collection,    /// using the default <see cref="EqualityComparer{T}"/>.    /// </summary>      /// <param name="collection">The items to fill the collection with, after clearing it.</param>    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>    public void ReplaceRange(IEnumerable<T> collection)    {      ReplaceRange(0, Count, collection, EqualityComparer<T>.Default);    }    /// <summary>    /// Clears the current collection and replaces it with the specified collection,    /// using the specified comparer to skip equal items.    /// </summary>    /// <param name="collection">The items to fill the collection with, after clearing it.</param>    /// <param name="comparer">An <see cref="IEqualityComparer{T}"/> to be used    /// to check whether an item in the same location already existed before,    /// which in case it would not be added to the collection, and no event will be raised for it.</param>    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>    /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>    public void ReplaceRange(IEnumerable<T> collection, IEqualityComparer<T> comparer)    {      ReplaceRange(0, Count, collection, comparer);    }    /// <summary>    /// Removes the specified range and inserts the specified collection,    /// ignoring equal items (using <see cref="EqualityComparer{T}.Default"/>).    /// </summary>    /// <param name="index">The index of where to start the replacement.</param>    /// <param name="count">The number of items to be replaced.</param>    /// <param name="collection">The collection to insert in that location.</param>    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>    /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>    public void ReplaceRange(int index, int count, IEnumerable<T> collection)    {      ReplaceRange(index, count, collection, EqualityComparer<T>.Default);    }    /// <summary>    /// Removes the specified range and inserts the specified collection in its position, leaving equal items in equal positions intact.    /// </summary>    /// <param name="index">The index of where to start the replacement.</param>    /// <param name="count">The number of items to be replaced.</param>    /// <param name="collection">The collection to insert in that location.</param>    /// <param name="comparer">The comparer to use when checking for equal items.</param>    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>    /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>    /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>    public void ReplaceRange(int index, int count, IEnumerable<T> collection, IEqualityComparer<T> comparer)    {      if (index < 0)        throw new ArgumentOutOfRangeException(nameof(index));      if (count < 0)        throw new ArgumentOutOfRangeException(nameof(count));      if (index + count > Count)        throw new ArgumentOutOfRangeException(nameof(index));      if (collection == null)        throw new ArgumentNullException(nameof(collection));      if (comparer == null)        throw new ArgumentNullException(nameof(comparer));      if (collection is ICollection<T> countable)      {        if (countable.Count == 0)        {          RemoveRange(index, count);          return;        }      }      else if (!ContainsAny(collection))      {        RemoveRange(index, count);        return;      }      if (index + count == 0)      {        InsertRange(0, collection);        return;      }      if (!(collection is IList<T> list))        list = new List<T>(collection);      using (BlockReentrancy())      using (DeferEvents())      {        var rangeCount = index + count;        var addedCount = list.Count;        var changesMade = false;        List<T> newCluster = null, oldCluster = null;        int i = index;        for (; i < rangeCount && i - index < addedCount; i++)        {          //parallel position          T old = this[i], @new = list[i - index];          if (comparer.Equals(old, @new))          { onRangeReplaced(i, newCluster, oldCluster); continue;          }          else          { Items[i] = @new; if (newCluster == null) {   Debug.Assert(oldCluster == null);   newCluster = new List<T> { @new };   oldCluster = new List<T> { old }; } else {   newCluster.Add(@new);   oldCluster.Add(old); } changesMade = true;          }        }        onRangeReplaced(i, newCluster, oldCluster);        //exceeding position        if (count != addedCount)        {          var items = (List<T>)Items;          if (count > addedCount)          { var removedCount = rangeCount - addedCount; T[] removed = new T[removedCount]; items.CopyTo(i, removed, 0, removed.Length); items.RemoveRange(i, removedCount); onCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, i));          }          else          { var k = i - index; T[] added = new T[addedCount - k]; for (int j = k; j < addedCount; j++) {   T @new = list[j];   added[j - k] = @new; } items.InsertRange(i, added); onCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, i));          }          onEssentialPropertiesChanged();        }        else if (changesMade)        {          onIndexerPropertyChanged();        }      }    }    #endregion Public Methods    //------------------------------------------------------    //    //  Protected Methods    //    //------------------------------------------------------    #region Protected Methods    /// <summary>    /// Called by base class Collection&lt;T&gt; when the list is being cleared;    /// raises a CollectionChanged event to any listeners.    /// </summary>    protected override void ClearItems()    {      if (Count == 0)        return;      CheckReentrancy();      base.ClearItems();      onEssentialPropertiesChanged();      onCollectionReset();    }    /// <summary>    /// Called by base class Collection&lt;T&gt; when an item is set in list;    /// raises a CollectionChanged event to any listeners.    /// </summary>    protected override void SetItem(int index, T item)    {      if (Equals(this[index], item))        return;      CheckReentrancy();      T originalItem = this[index];      base.SetItem(index, item);      onIndexerPropertyChanged();      onCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);    }    /// <summary>    /// Raise CollectionChanged event to any listeners.    /// Properties/methods modifying this ObservableCollection will raise    /// a collection changed event through this virtual method.    /// </summary>    /// <remarks>    /// When overriding this method, either call its base implementation    /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.    /// </remarks>    protected override void onCollectionChanged(NotifyCollectionChangedEventArgs e)    {      if (_deferredEvents != null)      {        _deferredEvents.Add(e);        return;      }      base.onCollectionChanged(e);    }    protected virtual IDisposable DeferEvents() => new DeferredEventsCollection(this);    #endregion Protected Methods    //------------------------------------------------------    //    //  Private Methods    //    //------------------------------------------------------    #region Private Methods    /// <summary>    /// Helper function to determine if a collection contains any elements.    /// </summary>    /// <param name="collection">The collection to evaluate.</param>    /// <returns></returns>    private static bool ContainsAny(IEnumerable<T> collection)    {      using (IEnumerator<T> enumerator = collection.GetEnumerator())        return enumerator.MoveNext();    }    /// <summary>    /// Helper to raise Count property and the Indexer property.    /// </summary>    private void onEssentialPropertiesChanged()    {      onPropertyChanged(EventArgsCache.CountPropertyChanged);      onIndexerPropertyChanged();    }    /// <summary>    /// /// Helper to raise a PropertyChanged event for the Indexer property    /// /// </summary>    private void onIndexerPropertyChanged() =>      onPropertyChanged(EventArgsCache.IndexerPropertyChanged);    /// <summary>    /// Helper to raise CollectionChanged event to any listeners    /// </summary>    private void onCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>      onCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));    /// <summary>    /// Helper to raise CollectionChanged event with action == Reset to any listeners    /// </summary>    private void onCollectionReset() =>      onCollectionChanged(EventArgsCache.ResetCollectionChanged);    /// <summary>    /// Helper to raise event for clustered action and clear cluster.    /// </summary>    /// <param name="followingItemIndex">The index of the item following the replacement block.</param>    /// <param name="newCluster"></param>    /// <param name="oldCluster"></param>    //TODO should have really been a local method inside ReplaceRange(int index, int count, IEnumerable<T> collection, IEqualityComparer<T> comparer),    //move when supported language version updated.    private void onRangeReplaced(int followingItemIndex, ICollection<T> newCluster, ICollection<T> oldCluster)    {      if (oldCluster == null || oldCluster.Count == 0)      {        Debug.Assert(newCluster == null || newCluster.Count == 0);        return;      }      onCollectionChanged(          new NotifyCollectionChangedEventArgs(   NotifyCollectionChangedAction.Replace,   new List<T>(newCluster),   new List<T>(oldCluster),   followingItemIndex - oldCluster.Count));      oldCluster.Clear();      newCluster.Clear();    }    #endregion Private Methods    //------------------------------------------------------    //    //  Private Types    //    //------------------------------------------------------    #region Private Types    private sealed class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable    {      private readonly ObservableRangeCollection<T> _collection;      public DeferredEventsCollection(ObservableRangeCollection<T> collection)      {        Debug.Assert(collection != null);        Debug.Assert(collection._deferredEvents == null);        _collection = collection;        _collection._deferredEvents = this;      }      public void Dispose()      {        _collection._deferredEvents = null;        foreach (var args in this)          _collection.onCollectionChanged(args);      }    }    #endregion Private Types  }  /// <remarks>  /// To be kept outside <see cref="ObservableCollection{T}"/>, since otherwise, a new instance will be created for each generic type used.  /// </remarks>  internal static class EventArgsCache  {    internal static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs("Count");    internal static readonly PropertyChangedEventArgs IndexerPropertyChanged = new PropertyChangedEventArgs("Item[]");    internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);  }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/411980.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号