【问题标题】:How to create a custom observable collection using ConcurrentDictionary, INotifyCollectionChanged, INotifyPropertyChanged如何使用 ConcurrentDictionary、INotifyCollectionChanged、INotifyPropertyChanged 创建自定义可观察集合
【发布时间】:2011-04-25 07:22:39
【问题描述】:

我正在尝试创建一个 ObservableConcurrentDictionary。该对象将用于多线程应用程序,其数据用于通过控件的 ItemsSource 属性填充控件。

这是我想出的实现:

public sealed class ObservableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
    #region Constructors

    public ObservableConcurrentDictionary()
        : base()
    { 

    }

    public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection)
        : base(collection)
    { 

    }


    public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer)
        : base(comparer)
    { 

    }


    public ObservableConcurrentDictionary(int concurrencyLevel, int capacity)
        : base(concurrencyLevel, capacity)
    { 

    }


    public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
        : base(collection, comparer)
    { 

    }


    public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
        : base(concurrencyLevel, capacity, comparer)
    { 

    }

    public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
        : base(concurrencyLevel, collection, comparer)
    {

    }

    #endregion

    #region Public Methods

    public new TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
    {
        // Stores the value
        TValue value;
        // If key exists
        if (base.ContainsKey(key))
        {
            // Update value and raise event
            value = base.AddOrUpdate(key, addValueFactory, updateValueFactory);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace));
        }
        // Else if key does not exist
        else
        {
            // Add value and raise event
            value = base.AddOrUpdate(key, addValueFactory, updateValueFactory);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
        }
        // Returns the value
        return value;
    }

    public void Clear()
    {
        // Clear dictionary
        base.Clear();
        // Raise event
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
    {
        // Stores the value
        TValue value;
        // If key exists
        if (base.ContainsKey(key))
            // Get value
            value = base.GetOrAdd(key, valueFactory);
        // Else if key does not exist
        else
        {
            // Add value and raise event
            value = base.GetOrAdd(key, valueFactory);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
        }
        // Return value
        return value;
    }

    public new TValue GetOrAdd(TKey key, TValue value)
    {
        // If key exists
        if (base.ContainsKey(key))
            // Get value
            base.GetOrAdd(key, value);
        // Else if key does not exist
        else
        {
            // Add value and raise event
            base.GetOrAdd(key, value);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
        }
        // Return value
        return value;
    }

    public new bool TryAdd(TKey key, TValue value)
    {
        // Stores tryAdd
        bool tryAdd;
        // If added
        if (tryAdd = base.TryAdd(key, value))
            // Raise event
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
        // Return tryAdd
        return tryAdd;
    }

    public new bool TryRemove(TKey key, out TValue value)
    {
        // Stores tryRemove
        bool tryRemove;
        // If removed
        if (tryRemove = base.TryRemove(key, out value))
            // Raise event
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove));
        // Return tryAdd
        return tryRemove;
    }

    public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
    {
        // Stores tryUpdate
        bool tryUpdate;
        // If updated
        if (tryUpdate = base.TryUpdate(key, newValue, comparisonValue))
            // Raise event
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace));
        // Return tryUpdate
        return tryUpdate;
    }

    #endregion

    #region Private Methods

    private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
            CollectionChanged(this, e);
    }

    #endregion

    #region INotifyCollectionChanged Members

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

不幸的是,该解决方案没有按预期工作 - 事实上,它根本不起作用。关于我做错了什么或有什么更好的解决方案有什么想法吗?

请注意我不能使用 ObservableCollection,因此我必须编写自己的 Observable 集合。

编辑: 工作版本如下。希望这可以帮助遇到类似问题的其他人。

public sealed class ObservableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
    public ObservableConcurrentDictionary()
        : base()
    { 

    }

    public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection)
        : base(collection)
    { 

    }

    public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer)
        : base(comparer)
    { 

    }

    public ObservableConcurrentDictionary(int concurrencyLevel, int capacity)
        : base(concurrencyLevel, capacity)
    { 

    }

    public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
        : base(collection, comparer)
    { 

    }

    public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
        : base(concurrencyLevel, capacity, comparer)
    { 

    }

    public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
        : base(concurrencyLevel, collection, comparer)
    {

    }

    public new TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
    {
        // Stores the value
        TValue value;
        // If key exists
        if (base.ContainsKey(key))
        {
            // Update value and raise event
            value = base.AddOrUpdate(key, addValueFactory, updateValueFactory);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value));
        }
        // Else if key does not exist
        else
        {
            // Add value and raise event
            value = base.AddOrUpdate(key, addValueFactory, updateValueFactory);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
        }
        // Returns the value
        return value;
    }

    public new void Clear()
    {
        // Clear dictionary
        base.Clear();
        // Raise event
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
    {
        // Stores the value
        TValue value;
        // If key exists
        if (base.ContainsKey(key))
            // Get value
            value = base.GetOrAdd(key, valueFactory);
        // Else if key does not exist
        else
        {
            // Add value and raise event
            value = base.GetOrAdd(key, valueFactory);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
        }
        // Return value
        return value;
    }

    public new TValue GetOrAdd(TKey key, TValue value)
    {
        // If key exists
        if (base.ContainsKey(key))
            // Get value
            base.GetOrAdd(key, value);
        // Else if key does not exist
        else
        {
            // Add value and raise event
            base.GetOrAdd(key, value);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
        }
        // Return value
        return value;
    }

    public new bool TryAdd(TKey key, TValue value)
    {
        // Stores tryAdd
        bool tryAdd;
        // If added
        if (tryAdd = base.TryAdd(key, value))
            // Raise event
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
        // Return tryAdd
        return tryAdd;
    }

    public new bool TryRemove(TKey key, out TValue value)
    {
        // Stores tryRemove
        bool tryRemove;
        // If removed
        if (tryRemove = base.TryRemove(key, out value))
            // Raise event
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value));
        // Return tryAdd
        return tryRemove;
    }

    public new bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
    {
        // Stores tryUpdate
        bool tryUpdate;
        // If updated
        if (tryUpdate = base.TryUpdate(key, newValue, comparisonValue))
            // Raise event
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue));
        // Return tryUpdate
        return tryUpdate;
    }

    private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
            CollectionChanged(this, e);
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public event PropertyChangedEventHandler PropertyChanged;
}

【问题讨论】:

  • “它根本不起作用” - 不是有效的问题描述。请告诉我们您遇到了什么错误,您使用什么代码来测试它以及您不能使用 ObservableCollection 的原因。

标签: .net wpf inotifypropertychanged inotifycollectionchanged interface-implementation


【解决方案1】:

在没有任何解释的情况下快速浏览您的代码我只能猜测。 我认为在 NotifyCollectionChangedEventArgs 上设置 Action 是不够的。还有NewItemsOldItems 属性,告诉订阅者哪些项目发生了变化。

另请注意,虽然这些是集合,但许多 WPF 组件一次仅支持通过 DataBinding 更改单个项目。

【讨论】:

  • 对于模糊的描述,我深表歉意。昨天是漫长而令人沮丧的一天。你让我朝着正确的方向前进。现在一切正常。谢谢!
【解决方案2】:

我开发了 ObservableConcurrentDictionnary 的紧凑版本,请评论/建议...

... where TValue : Object {使用你的类而不是 Object...}

古莱特

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Collections
{
    public class ObservableConcurrentDictionary<TValue> : ConcurrentDictionary<Int32, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
        where TValue : Object , new()
    {
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs changeAction)
        {
            var eh = CollectionChanged;
            if (eh == null) return;

            eh(this, changeAction);

            OnPropertyChanged();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged()
        {
            var eh = PropertyChanged;
            if (eh == null) return;

            // All properties : Keys, Values, Count, IsEmpty
            eh(this, new PropertyChangedEventArgs(null));
        }

        #region Ctors
        public ObservableConcurrentDictionary()
            : base()
        {

        }

        public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<Int32, TValue>> collection)
            : base(collection)
        {

        }

        public ObservableConcurrentDictionary(IEqualityComparer<Int32> comparer)
            : base(comparer)
        {

        }

        public ObservableConcurrentDictionary(int concurrencyLevel, int capacity)
            : base(concurrencyLevel, capacity)
        {

        }

        public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<Int32, TValue>> collection, IEqualityComparer<Int32> comparer)
            : base(collection, comparer)
        {

        }

        public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<Int32> comparer)
            : base(concurrencyLevel, capacity, comparer)
        {

        }

        public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<Int32, TValue>> collection, IEqualityComparer<Int32> comparer)
            : base(concurrencyLevel, collection, comparer)
        {

        }
        #endregion

        public new void Clear()
        {
            // Clear dictionary
            base.Clear();
            // Raise event
            OnCollectionChanged(changeAction: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public new TValue AddOrUpdate(Int32 key, Func<Int32, TValue> addValueFactory,
            Func<Int32, TValue, TValue> updateValueFactory)
        {
            bool isUpdated = false;
            TValue oldValue = default(TValue);

            TValue value = base.AddOrUpdate(key, addValueFactory, (k, v) =>
            {
                isUpdated = true;
                oldValue = v;
                return updateValueFactory(k, v);
            });

            if (isUpdated) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue));

            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
            return value;
        }

        public new TValue AddOrUpdate(Int32 key, TValue addValue, Func<Int32, TValue, TValue> updateValueFactory)
        {
            bool isUpdated = false;
            TValue oldValue = default(TValue);

            TValue value = base.AddOrUpdate(key, addValue, (k, v) =>
            {
                isUpdated = true;
                oldValue = v;
                return updateValueFactory(k, v);
            });

            if (isUpdated) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue));

            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
            return value;
        }

        public new TValue GetOrAdd(Int32 key, Func<Int32, TValue> addValueFactory)
        {
            bool isAdded = false;

            TValue value = base.GetOrAdd(key, k =>
            {
                isAdded = true;
                return addValueFactory(k);
            });

            if (isAdded) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));

            return value;
        }

        public new TValue GetOrAdd(Int32 key, TValue value)
        {
            return GetOrAdd(key, k => value);
        }

        public new bool TryAdd(Int32 key, TValue value)
        {
            bool tryAdd = base.TryAdd(key, value);

            if (tryAdd) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));

            return tryAdd;
        }

        public new bool TryRemove(Int32 key, out TValue value)
        {
            // Stores tryRemove
            bool tryRemove = base.TryRemove(key, out value);

            // If removed raise event
            if (tryRemove) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value));

            return tryRemove;
        }

        public new bool TryUpdate(Int32 key, TValue newValue, TValue comparisonValue)
        {
            // Stores tryUpdate
            bool tryUpdate = base.TryUpdate(key, newValue, comparisonValue);

            if (tryUpdate) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, comparisonValue));

            return tryUpdate;
        }

    }
}

【讨论】:

    【解决方案3】:

    对于那些仍然感兴趣的人,下面是一个完整的实现,从 @MStack 和 @c0D3l0g1c 的工作开始,并添加了文档、INotifyPropertyChanged 的实现以及 >= .NET 4.7 中的新方法。

    /// <summary>
    /// Represents a thread-safe collection of key/value pairs that can be accessed by multiple threads concurrently and provides
    /// notifications when items get added, removed, or when the whole list is refreshed.
    /// </summary>
    /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
    public sealed class ObservableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
    {
       private const string IndexerName = "Item[]";
    
       /// <summary>
       /// Initializes a new instance of the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> class that is empty, has the
       /// default concurrency level, has the default initial capacity, and uses the default comparer for the key type.
       /// </summary>
       /// <remarks>
       /// The default concurrency level is equal to the number of CPUs. The higher the concurrency level is, the more concurrent write
       /// operations can take place without interference and blocking. Higher concurrency level values also cause operations that require
       /// all locks (for example, table resizing, ToArray and Count) to become more expensive. The default capacity (DEFAULT_CAPACITY),
       /// which represents the initial number of buckets, is a trade-off between the size of a very small dictionary and the number of
       /// resizes when constructing a large dictionary. Also, the capacity should not be divisible by a small prime number. The default
       /// capacity is 31.
       /// </remarks>
       public ObservableConcurrentDictionary() : base()
       {
       }
    
       /// <summary>Initializes a new instance of the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> class that contains
       /// elements copied from the specified IEnumerable<T>, has the default concurrency level, has the default initial capacity, and uses
       /// the default comparer for the key type.</summary> <param name="collection">The <see cref="IEnumerable{T}" /> whose elements are
       /// copied to the new <see cref="ObservableConcurrentDictionary{TKey, TValue}" />.</param>
       public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : base(collection)
       {
       }
    
       /// <summary>
       /// Initializes a new instance of the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> class that is empty, has the
       /// default concurrency level and capacity, and uses the specified <see cref="IEqualityComparer{T}"/>.
       /// </summary>
       /// <param name="comparer">The equality comparison implementation to use when comparing keys.</param>
       public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer) : base(comparer)
       {
       }
    
       /// <summary>
       /// Initializes a new instance of the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> class that is empty, has the
       /// specified concurrency level and capacity, and uses the default comparer for the key type.
       /// </summary>
       /// <param name="concurrencyLevel">
       /// The estimated number of threads that will update the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> concurrently.
       /// </param>
       /// <param name="capacity">
       /// The initial number of elements that the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> can contain.
       /// </param>
       public ObservableConcurrentDictionary(int concurrencyLevel, int capacity) : base(concurrencyLevel, capacity)
       {
       }
    
       /// <summary>
       /// Initializes a new instance of the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> class that contains elements copied
       /// from the specified <see cref="IEnumerable{T}"/> has the default concurrency level, has the default initial capacity, and uses
       /// the specified <see cref="IEqualityComparer{T}"/>.
       /// </summary>
       /// <param name="collection">
       /// The <see cref="IEnumerable{T}"/> whose elements are copied to the new <see cref="ObservableConcurrentDictionary{TKey, TValue}"/>.
       /// </param>
       /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> implementation to use when comparing keys.</param>
       public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
          : base(collection, comparer)
       {
       }
    
       /// <summary>
       /// Initializes a new instance of the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> class that is empty, has the
       /// specified concurrency level, has the specified initial capacity, and uses the specified <see cref="IEqualityComparer{T}"/>.
       /// </summary>
       /// <param name="concurrencyLevel">
       /// The estimated number of threads that will update the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> concurrently.
       /// </param>
       /// <param name="capacity">
       /// The initial number of elements that the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> can contain.
       /// </param>
       /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> implementation to use when comparing keys.</param>
       public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
          : base(concurrencyLevel, capacity, comparer)
       {
       }
    
       /// <summary>
       /// Initializes a new instance of the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> class that contains elements copied
       /// from the specified IEnumerable, and uses the specified <see cref="IEqualityComparer{T}"/>.
       /// </summary>
       /// <param name="concurrencyLevel">
       /// The estimated number of threads that will update the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> concurrently.
       /// </param>
       /// <param name="collection">
       /// The <see cref="IEnumerable{T}"/> whose elements are copied to the new <see cref="ObservableConcurrentDictionary{TKey, TValue}"/>.
       /// </param>
       /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> implementation to use when comparing keys.</param>
       public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
          : base(concurrencyLevel, collection, comparer)
       {
       }
    
       /// <summary>Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.</summary>
       public event NotifyCollectionChangedEventHandler CollectionChanged;
    
       /// <summary>Occurs when a property value changes.</summary>
       public event PropertyChangedEventHandler PropertyChanged;
    
       /// <summary>
       /// Uses the specified functions to add a key/value pair to the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> if the
       /// key does not already exist, or to update a key/value pair in the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> if
       /// the key already exists.
       /// </summary>
       /// <param name="key">The key to be added or whose value should be updated</param>
       /// <param name="addValueFactory">The function used to generate a value for an absent key</param>
       /// <param name="updateValueFactory">
       /// The function used to generate a new value for an existing key based on the key's existing value
       /// </param>
       /// <returns>
       /// The new value for the key. This will be either be the result of <paramref name="addValueFactory"/> (if the key was absent) or
       /// the result of <paramref name="updateValueFactory"/> (if the key was present).
       /// </returns>
       public new TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
       {
          var add = !ContainsKey(key);
          var value = base.AddOrUpdate(key, addValueFactory, updateValueFactory);
          OnCollectionChanged(new NotifyCollectionChangedEventArgs(add ? NotifyCollectionChangedAction.Add : NotifyCollectionChangedAction.Replace, value));
          return value;
       }
    
       /// <summary>
       /// Adds a key/value pair to the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> if the key does not already exist, or
       /// updates a key/value pair in the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> by using the specified function if
       /// the key already exists.
       /// </summary>
       /// <param name="key">The key to be added or whose value should be updated</param>
       /// <param name="addValue">The value to be added for an absent key</param>
       /// <param name="updateValueFactory">
       /// The function used to generate a new value for an existing key based on the key's existing value
       /// </param>
       /// <returns>
       /// The new value for the key. This will be either be <paramref name="addValue"/> (if the key was absent) or the result of <paramref
       /// name="updateValueFactory"/> (if the key was present).
       /// </returns>
       public new TValue AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
       {
          var add = !ContainsKey(key);
          var value = base.AddOrUpdate(key, addValue, updateValueFactory);
          OnCollectionChanged(new NotifyCollectionChangedEventArgs(add ? NotifyCollectionChangedAction.Add : NotifyCollectionChangedAction.Replace, value));
          return value;
       }
    
    #if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462)
       /// <summary>
       /// Uses the specified functions and argument to add a key/value pair to the <see cref="ObservableConcurrentDictionary{TKey,
       /// TValue}"/> if the key does not already exist, or to update a key/value pair in the <see
       /// cref="ObservableConcurrentDictionary{TKey, TValue}"/> if the key already exists.
       /// </summary>
       /// <typeparam name="TArg">The type of an argument to pass into <paramref name="addValueFactory"/> and <paramref name="updateValueFactory"/>.</typeparam>
       /// <param name="key">The key to be added or whose value should be updated.</param>
       /// <param name="addValueFactory">The function used to generate a value for an absent key.</param>
       /// <param name="updateValueFactory">
       /// The function used to generate a new value for an existing key based on the key's existing value.
       /// </param>
       /// <param name="factoryArgument">An argument to pass into <paramref name="addValueFactory"/> and <paramref name="updateValueFactory"/>.</param>
       /// <returns>
       /// The new value for the key. This will be either be the result of <paramref name="addValueFactory"/> (if the key was absent) or
       /// the result of <paramref name="updateValueFactory"/> (if the key was present).
       /// </returns>
       public new TValue AddOrUpdate<TArg>(TKey key, Func<TKey, TArg, TValue> addValueFactory, Func<TKey, TValue, TArg, TValue> updateValueFactory, TArg factoryArgument)
       {
          var add = !ContainsKey(key);
          var value = base.AddOrUpdate(key, addValueFactory, updateValueFactory, factoryArgument);
          OnCollectionChanged(new NotifyCollectionChangedEventArgs(add ? NotifyCollectionChangedAction.Add : NotifyCollectionChangedAction.Replace, value));
          return value;
       }
    #endif
    
       /// <summary>Removes all keys and values from the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/>.</summary>
       public new void Clear()
       {
          base.Clear();
          OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
       }
    
       /// <summary>
       /// Adds a key/value pair to the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> by using the specified function if the
       /// key does not already exist. Returns the new value, or the existing value if the key exists.
       /// </summary>
       /// <param name="key">The key of the element to add.</param>
       /// <param name="valueFactory">The function used to generate a value for the key.</param>
       /// <returns>
       /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, or the new
       /// value if the key was not in the dictionary.
       /// </returns>
       public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
       {
          var add = !ContainsKey(key);
          var value = base.GetOrAdd(key, valueFactory);
          if (add) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
          return value;
       }
    
       /// <summary>
       /// Adds a key/value pair to the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> if the key does not already exist.
       /// Returns the new value, or the existing value if the key exists.
       /// </summary>
       /// <param name="key">The key of the element to add.</param>
       /// <param name="value">The value to be added, if the key does not already exist.</param>
       /// <returns>
       /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, or the new
       /// value if the key was not in the dictionary.
       /// </returns>
       public new TValue GetOrAdd(TKey key, TValue value)
       {
          var add = !ContainsKey(key);
          base.GetOrAdd(key, value);
          if (add) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
          return value;
       }
    
    #if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462)
       /// <summary>
       /// Adds a key/value pair to the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/> by using the specified function and an
       /// argument if the key does not already exist, or returns the existing value if the key exists.
       /// </summary>
       /// <typeparam name="TArg">The type of an argument to pass into <paramref name="valueFactory"/>.</typeparam>
       /// <param name="key">The key of the element to add.</param>
       /// <param name="valueFactory">The function used to generate a value for the key.</param>
       /// <param name="factoryArgument">An argument value to pass into <paramref name="valueFactory"/>.</param>
       /// <returns>
       /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, or the new
       /// value if the key was not in the dictionary.
       /// </returns>
       public new TValue GetOrAdd<TArg>(TKey key, Func<TKey, TArg, TValue> valueFactory, TArg factoryArgument)
       {
          var add = !ContainsKey(key);
          var value = base.GetOrAdd(key, valueFactory, factoryArgument);
          if (add) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
          return value;
       }
    #endif
    
       /// <summary>Attempts to add the specified key and value to the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/>.</summary>
       /// <param name="key">The key of the element to add.</param>
       /// <param name="value">The value of the element to add. The value can be <see langword="null"/> for reference types.</param>
       /// <returns>
       /// <see langword="true"/> if the key/value pair was added to the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/>
       /// successfully; <see langword="false"/> if the key already exists.
       /// </returns>
       public new bool TryAdd(TKey key, TValue value)
       {
          if (base.TryAdd(key, value))
          {
             OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
             return true;
          }
          return false;
       }
    
       /// <summary>
       /// Attempts to remove and return the value that has the specified key from the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/>.
       /// </summary>
       /// <param name="key">The key of the element to remove and return.</param>
       /// <param name="value">
       /// When this method returns, contains the object removed from the <see cref="ObservableConcurrentDictionary{TKey, TValue}"/>, or
       /// the default value of the <typeparamref name="TValue"/> type if <paramref name="key"/> does not exist.
       /// </param>
       /// <returns><see langword="true"/> if the object was removed successfully; otherwise, <see langword="false"/>.</returns>
       public new bool TryRemove(TKey key, out TValue value)
       {
          if (base.TryRemove(key, out value))
          {
             OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value));
             return true;
          }
          return false;
       }
    
       /// <summary>
       /// Updates the value associated with <paramref name="key"/> to <paramref name="newValue"/> if the existing value with <paramref
       /// name="key"/> is equal to <paramref name="comparisonValue"/>.
       /// </summary>
       /// <param name="key">The key of the value that is compared with <paramref name="comparisonValue"/> and possibly replaced.</param>
       /// <param name="newValue">
       /// The value that replaces the value of the element that has the specified <paramref name="key"/> if the comparison results in equality.
       /// </param>
       /// <param name="comparisonValue">The value that is compared with the value of the element that has the specified <paramref name="key"/>.</param>
       /// <returns>
       /// <see langword="true"/> if the value with <paramref name="key"/> was equal to <paramref name="comparisonValue"/> and was replaced
       /// with <paramref name="newValue"/>; otherwise, <see langword="false"/>.
       /// </returns>
       public new bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
       {
          if (base.TryUpdate(key, newValue, comparisonValue))
          {
             OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue));
             return true;
          }
          return false;
       }
    
       /// <summary>Raises the <see cref="CollectionChanged"/> event.</summary>
       /// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
       private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
       {
          if (e.Action != NotifyCollectionChangedAction.Replace) OnPropertyChanged(nameof(Count));
          OnPropertyChanged(IndexerName);
          CollectionChanged?.Invoke(this, e);
       }
    
       /// <summary>Raises the <see cref="PropertyChanged" /> event.</summary>
       /// <param name="propertyName">Name of the property that has changed.</param>
       private void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-12
      • 1970-01-01
      • 2013-07-02
      • 1970-01-01
      • 2012-04-28
      • 2014-11-29
      相关资源
      最近更新 更多