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() { _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)); _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) { _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)); _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(int concurrencyLevel, int capacity) { _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(int concurrencyLevel, IEnumerable> collection, IEqualityComparer comparer) { if (collection == null) throw new ArgumentNullException(nameof(collection)); _dictionary = new ConcurrentDictionary( concurrencyLevel, collection.Where(pair => pair.Value != null), comparer); } #endregion #region Public APIs /// public int Count => _dictionary.Count; /// public bool IsEmpty => _dictionary.IsEmpty; /// public ICollection Keys => _dictionary.Keys; /// public ICollection Values => _dictionary.Values; /// public TValue? this[TKey key] { get => _dictionary.TryGetValue(key ?? throw new ArgumentNullException(nameof(key)), out var value) ? value : null; set { if (value != null) { _dictionary[key] = value; } else { _dictionary.TryRemove(key, out _); } } } /// public void Clear() => _dictionary.Clear(); /// public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key); /// public TValue? GetOrAdd(TKey key, TValue value) { if (key == null) throw new ArgumentNullException(nameof(key)); if (value != null) return _dictionary.GetOrAdd(key, value); return _dictionary.TryGetValue(key, out var retrievedValue) ? retrievedValue : null; } /// public bool Remove(TKey key) => _dictionary.TryRemove(key, out _); /// public bool TryAdd(TKey key, TValue value) { if (key == null) throw new ArgumentNullException(nameof(key)); return value == null || _dictionary.TryAdd(key, value); } /// public bool TryGetValue(TKey key, out TValue value) => _dictionary.TryGetValue(key, out value); /// public bool TryRemove(TKey key, out TValue value) => _dictionary.TryRemove(key, out value); /// public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { if (key == null) throw new ArgumentNullException(nameof(key)); return newValue != null && comparisonValue != null && _dictionary.TryUpdate(key, newValue, comparisonValue); } #endregion #region Implementation of IDictionary /// void IDictionary.Add(TKey key, TValue value) { if (value != null) { ((IDictionary)_dictionary).Add(key, value); } else { _dictionary.TryRemove(key, out _); } } #endregion #region Implementation of IReadOnlyDictionary /// IEnumerable IReadOnlyDictionary.Keys => _dictionary.Keys; /// IEnumerable IReadOnlyDictionary.Values => _dictionary.Values; #endregion #region Implementation of ICollection> /// /// /// This property is always for a . /// bool ICollection>.IsReadOnly => false; /// void ICollection>.Add(KeyValuePair item) { if (item.Value != null) { ((ICollection>)_dictionary).Add(item); } else { _dictionary.TryRemove(item.Key, out _); } } /// bool ICollection>.Contains(KeyValuePair item) => ((ICollection>)_dictionary).Contains(item); /// void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) => ((ICollection>)_dictionary).CopyTo(array, arrayIndex); /// bool ICollection>.Remove(KeyValuePair item) => ((ICollection>)_dictionary).Remove(item); #endregion #region Implementation of IEnumerable> /// IEnumerator> IEnumerable>.GetEnumerator() => _dictionary.GetEnumerator(); #endregion #region Implementation of IEnumerable /// IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_dictionary).GetEnumerator(); #endregion } }