using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Swan.Collections {
///
/// Represents a non-thread-safe collection of key/value pairs that does not store null values.
///
/// 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 DataDictionary : IDataDictionary
where TKey : class
where TValue : class {
#region Private data
private readonly Dictionary _dictionary;
#endregion
#region Instance management
///
/// Initializes a new instance of the class
/// that is empty, has the default initial capacity,
/// and uses the default comparer for .
///
///
public DataDictionary() => this._dictionary = new Dictionary();
///
/// Initializes a new instance of the class
/// that contains elements copied from the specified ,
/// 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 DataDictionary(IEnumerable> collection) {
if(collection == null) {
throw new ArgumentNullException(nameof(collection));
}
this._dictionary = new Dictionary();
foreach(KeyValuePair pair in collection.Where(pair => pair.Value != null)) {
this._dictionary.Add(pair.Key, pair.Value);
}
}
///
/// Initializes a new instance of the class
/// that is empty, has the default capacity, and uses the specified .
///
/// The equality comparison implementation to use when comparing keys.
/// is .
///
public DataDictionary(IEqualityComparer comparer) => this._dictionary = new Dictionary(comparer);
///
/// Initializes a new instance of the class
/// that contains elements copied from the specified ,
/// 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 DataDictionary(IEnumerable> collection, IEqualityComparer comparer) {
if(collection == null) {
throw new ArgumentNullException(nameof(collection));
}
this._dictionary = new Dictionary(comparer);
foreach(KeyValuePair pair in collection.Where(pair => pair.Value != null)) {
this._dictionary.Add(pair.Key, pair.Value);
}
}
///
/// Initializes a new instance of the class
/// that is empty, has the specified capacity, and uses the default comparer for the key type.
///
/// The initial number of elements that the can contain.
/// is less than 0.
///
public DataDictionary(Int32 capacity) => this._dictionary = new Dictionary(capacity);
///
/// Initializes a new instance of the class
/// that contains elements copied from the specified ,
/// has the specified capacity, and uses the specified .
///
/// The initial number of elements that the can contain.
/// 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 0.
///
public DataDictionary(Int32 capacity, IEnumerable> collection, IEqualityComparer comparer) {
if(collection == null) {
throw new ArgumentNullException(nameof(collection));
}
this._dictionary = new Dictionary(capacity, comparer);
foreach(KeyValuePair pair in collection.Where(pair => pair.Value != null)) {
this._dictionary.Add(pair.Key, pair.Value);
}
}
#endregion
#region Public APIs
///
public Int32 Count => this._dictionary.Count;
///
public Boolean IsEmpty => this._dictionary.Count == 0;
///
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.Remove(key);
}
}
}
///
public void Clear() => this._dictionary.Clear();
///
public Boolean ContainsKey(TKey key) =>
// _dictionary.ContainsKey will take care of throwing on a null key.
this._dictionary.ContainsKey(key);
///
public TValue GetOrAdd(TKey key, TValue value) {
// _dictionary.TryGetValue will take care of throwing on a null key.
if(this._dictionary.TryGetValue(key, out TValue result)) {
return result;
}
if(value == null) {
return null;
}
this._dictionary.Add(key, value);
return value;
}
///
public Boolean Remove(TKey key) =>
// _dictionary.Remove will take care of throwing on a null key.
this._dictionary.Remove(key);
///
public Boolean TryAdd(TKey key, TValue value) {
// _dictionary.ContainsKey will take care of throwing on a null key.
if(this._dictionary.ContainsKey(key)) {
return false;
}
if(value != null) {
this._dictionary.Add(key, value);
}
return true;
}
///
public Boolean TryGetValue(TKey key, out TValue value) => this._dictionary.TryGetValue(key, out value);
///
public Boolean TryRemove(TKey key, out TValue value) {
// TryGetValue will take care of throwing on a null key.
if(!this._dictionary.TryGetValue(key, out value)) {
return false;
}
_ = this._dictionary.Remove(key);
return true;
}
///
public Boolean TryUpdate(TKey key, TValue newValue, TValue comparisonValue) {
// TryGetValue will take care of throwing on a null key.
if(!this._dictionary.TryGetValue(key, out TValue value)) {
return false;
}
if(value != comparisonValue) {
return false;
}
this._dictionary[key] = newValue;
return true;
}
#endregion
#region Implementation of IDictionary
///
void IDictionary.Add(TKey key, TValue value) {
// Validating the key seems redundant, because both Add and Remove
// will throw on a null key.
// This way, though, the code path on null key does not depend on value.
// Without this validation, there should be two unit tests for null key,
// one with a null value and one with a non-null value,
// which makes no sense.
if(key == null) {
throw new ArgumentNullException(nameof(key));
}
if(value != null) {
this._dictionary.Add(key, value);
} else {
_ = this._dictionary.Remove(key);
}
}
#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.Remove(item.Key);
}
}
///
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
}
}