Dot-Net

如何使用 ConcurrentDictionary、INotifyCollectionChanged、INotifyPropertyChanged 創建自定義可觀察集合

  • January 29, 2020

我正在嘗試創建一個 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;
}

我只能猜測,在沒有任何解釋的情況下快速瀏覽您的程式碼。我認為在 NotifyCollectionChangedEventArgs 上設置 Action 是不夠的。還有NewItems,OldItems屬性,告訴訂閱者哪些項目發生了變化。

另請注意,雖然這些是集合,但許多 WPF 組件一次僅支持通過 DataBinding 更改單個項目。

我開發了 ObservableConcurrentDictionnary 的緊湊版本,請評論/建議…

…其中 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;
       }

   }
}

引用自:https://stackoverflow.com/questions/3931716