How to create a custom observable collection using ConcurrentDictionary, INotifyCollectionChanged, INotifyPropertyChanged
I am trying to create an ObservableConcurrentDictionary. This object will be used in a multithreaded application, and it's data is used to populate a control via the controls ItemsSource property.
This is the implementation i have come up with:
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
}
Unfortunately the solution does not work as intended - in fact, it doesn't work at all. Any ideas on what i am doing wrong or do any better solutions exist?
Please note i CAN'T USE ObservableCollection, hence i have to write my own Observable collection.
EDIT: The working version is below. Hope this helps someone else with a similar problem.
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));
开发者_StackOverflow }
// 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;
}
Quickly going through your code without any eplanation from your side I can only guess.
I dont think setting Action on NotifyCollectionChangedEventArgs is enough. There are also NewItems
, OldItems
properties, that tell subscriber what items got changed.
Also note that, while those are collections, many WPF components support only single item change at a time through DataBinding.
I developed a tight version of an ObservableConcurrentDictionnary, please comment/suggest...
... where TValue : Object {use your class instead of Object...}
Qurlet
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;
}
}
}
For those still interested, below is a full implementation starting from the work of @MStack and @c0D3l0g1c and adding documentation, implementation of INotifyPropertyChanged
, and new methods in >= .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));
}
精彩评论