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
}
}