using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace Swan.Collections { /// /// Represents a thread-safe collection of key/value pairs that does not store null values /// and can be accessed by multiple threads concurrently. /// /// The type of keys in the dictionary. This must be a reference type. /// The type of values in the dictionary. This must be a reference type. /// public sealed class ConcurrentDataDictionary : IDataDictionary where TKey : class where TValue : class { #region Private data private readonly ConcurrentDictionary _dictionary; #endregion #region Instance management /// /// Initializes a new instance of the class /// that is empty, has the default concurrency level, has the default initial capacity, /// and uses the default comparer for . /// /// public ConcurrentDataDictionary() => this._dictionary = new ConcurrentDictionary(); /// /// Initializes a new instance of the class /// that contains elements copied from the specified , has the default concurrency level, /// has the default initial capacity, and uses the default comparer for . /// /// The whose elements are copied /// to the new . /// is . /// /// Since does not store null values, /// key/value pairs whose value is will not be copied from . /// /// public ConcurrentDataDictionary(IEnumerable> collection) { if(collection == null) { throw new ArgumentNullException(nameof(collection)); } this._dictionary = new ConcurrentDictionary(collection.Where(pair => pair.Value != null)); } /// /// Initializes a new instance of the class /// that is empty, has the default concurrency level and capacity, and uses the specified . /// /// The equality comparison implementation to use when comparing keys. /// is . /// public ConcurrentDataDictionary(IEqualityComparer comparer) => this._dictionary = new ConcurrentDictionary(comparer); /// /// Initializes a new instance of the class /// that contains elements copied from the specified , has the default concurrency level, /// has the default initial capacity, and uses the specified . /// /// The whose elements are copied /// to the new . /// The equality comparison implementation to use when comparing keys. /// /// Since does not store null values, /// key/value pairs whose value is will not be copied from . /// /// /// is . /// - or -. /// is . /// /// public ConcurrentDataDictionary(IEnumerable> collection, IEqualityComparer comparer) { if(collection == null) { throw new ArgumentNullException(nameof(collection)); } this._dictionary = new ConcurrentDictionary(collection.Where(pair => pair.Value != null), comparer); } /// /// Initializes a new instance of the class /// that is empty, has the specified concurrency level and capacity, and uses the default comparer for the key type. /// /// The estimated number of threads that will update /// the concurrently. /// The initial number of elements that the can contain. /// /// is less than 1. /// - or -. /// is less than 0. /// /// public ConcurrentDataDictionary(Int32 concurrencyLevel, Int32 capacity) => this._dictionary = new ConcurrentDictionary(concurrencyLevel, capacity); /// /// Initializes a new instance of the class /// that contains elements copied from the specified , has the specified concurrency level, /// has the default initial capacity, and uses the specified . /// /// The estimated number of threads that will update /// the concurrently. /// The whose elements are copied /// to the new . /// The equality comparison implementation to use when comparing keys. /// /// Since does not store null values, /// key/value pairs whose value is will not be copied from . /// /// /// is . /// - or -. /// is . /// /// is less than 1. /// public ConcurrentDataDictionary(Int32 concurrencyLevel, IEnumerable> collection, IEqualityComparer comparer) { if(collection == null) { throw new ArgumentNullException(nameof(collection)); } this._dictionary = new ConcurrentDictionary(concurrencyLevel, collection.Where(pair => pair.Value != null), comparer); } #endregion #region Public APIs /// public Int32 Count => this._dictionary.Count; /// public Boolean IsEmpty => this._dictionary.IsEmpty; /// public ICollection Keys => this._dictionary.Keys; /// public ICollection Values => this._dictionary.Values; /// public TValue this[TKey key] { get => this._dictionary.TryGetValue(key ?? throw new ArgumentNullException(nameof(key)), out TValue value) ? value : null; set { if(value != null) { this._dictionary[key] = value; } else { _ = this._dictionary.TryRemove(key, out _); } } } /// public void Clear() => this._dictionary.Clear(); /// public Boolean ContainsKey(TKey key) => this._dictionary.ContainsKey(key); /// public TValue GetOrAdd(TKey key, TValue value) { if(key == null) { throw new ArgumentNullException(nameof(key)); } return value != null ? this._dictionary.GetOrAdd(key, value) : this._dictionary.TryGetValue(key, out TValue retrievedValue) ? retrievedValue : null; } /// public Boolean Remove(TKey key) => this._dictionary.TryRemove(key, out _); /// public Boolean TryAdd(TKey key, TValue value) { if(key == null) { throw new ArgumentNullException(nameof(key)); } return value == null || this._dictionary.TryAdd(key, value); } /// public Boolean TryGetValue(TKey key, out TValue value) => this._dictionary.TryGetValue(key, out value); /// public Boolean TryRemove(TKey key, out TValue value) => this._dictionary.TryRemove(key, out value); /// public Boolean TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { if(key == null) { throw new ArgumentNullException(nameof(key)); } return newValue != null && comparisonValue != null && this._dictionary.TryUpdate(key, newValue, comparisonValue); } #endregion #region Implementation of IDictionary /// void IDictionary.Add(TKey key, TValue value) { if(value != null) { ((IDictionary)this._dictionary).Add(key, value); } else { _ = this._dictionary.TryRemove(key, out _); } } #endregion #region Implementation of IReadOnlyDictionary /// IEnumerable IReadOnlyDictionary.Keys => this._dictionary.Keys; /// IEnumerable IReadOnlyDictionary.Values => this._dictionary.Values; #endregion #region Implementation of ICollection> /// /// /// This property is always for a . /// Boolean ICollection>.IsReadOnly => false; /// void ICollection>.Add(KeyValuePair item) { if(item.Value != null) { ((ICollection>)this._dictionary).Add(item); } else { _ = this._dictionary.TryRemove(item.Key, out _); } } /// Boolean ICollection>.Contains(KeyValuePair item) => ((ICollection>)this._dictionary).Contains(item); /// void ICollection>.CopyTo(KeyValuePair[] array, Int32 arrayIndex) => ((ICollection>)this._dictionary).CopyTo(array, arrayIndex); /// Boolean ICollection>.Remove(KeyValuePair item) => ((ICollection>)this._dictionary).Remove(item); #endregion #region Implementation of IEnumerable> /// IEnumerator> IEnumerable>.GetEnumerator() => this._dictionary.GetEnumerator(); #endregion #region Implementation of IEnumerable /// IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this._dictionary).GetEnumerator(); #endregion } }