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