// Needed for NET30 #if !NET_4_6 using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.Serialization; //using System.Security.Permissions; using LinqInternal.Core; namespace LinqInternal.Collections.Specialized { [Serializable] [System.Diagnostics.DebuggerNonUserCode] [System.Diagnostics.DebuggerDisplay("Count={Count}")] internal sealed partial class NullAwareDictionary : IDictionary, ISerializable { private static readonly TKey _typedNull = TypeHelper.Cast(null); private readonly Dictionary _dictionary; private bool _hasNull; [NonSerialized] private ExtendedReadOnlyCollection _keys; [NonSerialized] private IReadOnlyDictionary _readOnly; [NonSerialized] private IEqualityComparer _valueComparer; private TValue[] _valueForNull; [NonSerialized] private ExtendedReadOnlyCollection _values; public NullAwareDictionary() { _dictionary = new Dictionary(); if (typeof(TKey).CanBeNull()) { InitializeNullable(); } else { InitializeNotNullable(); } } public NullAwareDictionary(IEqualityComparer comparer) { _dictionary = new Dictionary(comparer); if (typeof(TKey).CanBeNull()) { InitializeNullable(); } else { InitializeNotNullable(); } } public NullAwareDictionary(IDictionary dictionary) { if (dictionary == null) { throw new ArgumentNullException("dictionary", "dictionary is null."); } else { _dictionary = new Dictionary(dictionary); if (typeof(TKey).CanBeNull()) { InitializeNullable(); TakeValueForNull(dictionary); } else { InitializeNotNullable(); } } } public NullAwareDictionary(IDictionary dictionary, IEqualityComparer comparer) { if (dictionary == null) { throw new ArgumentNullException("dictionary", "dictionary is null."); } else { _dictionary = new Dictionary(dictionary, comparer); if (typeof(TKey).CanBeNull()) { InitializeNullable(); TakeValueForNull(dictionary); } else { InitializeNotNullable(); } } } //[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter)] private NullAwareDictionary(SerializationInfo info, StreamingContext context) : this() { if (info == null) { throw new ArgumentNullException("info"); } else { _dictionary = (Dictionary)info.GetValue("dictionary", typeof(Dictionary)); _hasNull = info.GetBoolean("_hasNull"); _valueForNull[0] = (TValue)info.GetValue("valueForNull", typeof(TValue)); } } public IReadOnlyDictionary AsReadOnly { get { return _readOnly; } } public IEqualityComparer Comparer { get { return _dictionary.Comparer; } } public int Count { get { return _hasNull ? _dictionary.Count + 1 : _dictionary.Count; } } public ICollection Keys { get { return _keys; } } public ICollection Values { get { return _values; } } bool ICollection>.IsReadOnly { get { return false; } } public TValue this[TKey key] { get { // key can be null if (ReferenceEquals(key, null)) { if (_hasNull) { return _valueForNull[0]; } else { throw new KeyNotFoundException(); } } else { return _dictionary[key]; } } set { // key can be null if (ReferenceEquals(key, null)) { SetForNull(value); } else { _dictionary[key] = value; } } } public void Add(TKey key, TValue value) { // key can be null if (ReferenceEquals(key, null)) { if (_hasNull) { throw new ArgumentException(); } else { SetForNull(value); } } else { _dictionary.Add(key, value); } } public void Add(KeyValuePair item) { var key = item.Key; var value = item.Value; Add(key, value); } public void Clear() { ClearForNull(); _dictionary.Clear(); } public bool Contains(KeyValuePair item) { var key = item.Key; var value = item.Value; if (ReferenceEquals(key, null)) { if (_hasNull) { return _valueComparer.Equals(_valueForNull[0], value); } else { return false; } } else { try { return _valueComparer.Equals(_dictionary[key], value); } catch (KeyNotFoundException) { return false; } } } public bool Contains(KeyValuePair item, IEqualityComparer> comparer) { return Enumerable.Contains(this, item, comparer); } public bool ContainsKey(TKey key) { // key can be null if (ReferenceEquals(key, null)) { return _hasNull; } else { return _dictionary.ContainsKey(key); } } public void CopyTo(KeyValuePair[] array, int arrayIndex) { Extensions.CanCopyTo(Count, array, arrayIndex); Extensions.CopyTo(this, array); } public void CopyTo(KeyValuePair[] array) { Extensions.CanCopyTo(Count, array); Extensions.CopyTo(this, array); } public void CopyTo(KeyValuePair[] array, int arrayIndex, int countLimit) { Extensions.CanCopyTo(array, arrayIndex, countLimit); Extensions.CopyTo(this, array, countLimit); } public void ExceptWith(IEnumerable> other) { Extensions.ExceptWith(this, other); } public IEnumerator> GetEnumerator() { if (_hasNull) { yield return new KeyValuePair( _typedNull, _valueForNull[0] ); } foreach (var item in _dictionary) { yield return item; } } //[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter)] public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("dictionary", _dictionary, typeof(Dictionary)); info.AddValue("hasNull", _hasNull); info.AddValue("valueForNull", _valueForNull[0], typeof(TValue)); } public void IntersectWith(IEnumerable> other) { Extensions.IntersectWith(this, other); } public bool IsProperSubsetOf(IEnumerable> other) { return Extensions.IsProperSubsetOf(this, other); } public bool IsProperSupersetOf(IEnumerable> other) { return Extensions.IsProperSupersetOf(this, other); } public bool IsSubsetOf(IEnumerable> other) { return Extensions.IsSubsetOf(this, other); } public bool IsSupersetOf(IEnumerable> other) { return Extensions.IsSupersetOf(this, other); } public bool Overlaps(IEnumerable> other) { return Extensions.Overlaps(this, other); } public bool Remove(TKey key) { // key can be null if (ReferenceEquals(key, null)) { if (_hasNull) { ClearForNull(); return true; } else { return false; } } else { return _dictionary.Remove(key); } } public bool Remove(KeyValuePair item) { var key = item.Key; var value = item.Value; if (ReferenceEquals(key, null)) { if (_valueComparer.Equals(_valueForNull[0], value)) { ClearForNull(); return true; } else { return false; } } else { try { if (_valueComparer.Equals(_dictionary[key], value)) { return _dictionary.Remove(key); } else { return false; } } catch (KeyNotFoundException) { return false; } } } public bool Remove(KeyValuePair item, IEqualityComparer> comparer) { if (ReferenceEquals(item.Key, null)) { if (_hasNull) { ClearForNull(); return true; } else { return false; } } else { return _dictionary.Remove(item, comparer); } } public bool SetEquals(IEnumerable> other) { return Extensions.SetEquals(this, other); } public void SymmetricExceptWith(IEnumerable> other) { Extensions.SymmetricExceptWith(this, other); } public bool TryGetValue(TKey key, out TValue value) { // key can be null if (ReferenceEquals(key, null)) { if (_hasNull) { value = _valueForNull[0]; return true; } else { value = default(TValue); return false; } } else { return _dictionary.TryGetValue(key, out value); } } public void UnionWith(IEnumerable> other) { if (other == null) { throw new ArgumentNullException("other"); } this.AddRange(other); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } private void ClearForNull() { _hasNull = false; _valueForNull = new[] { default(TValue) }; } private void InitializeNotNullable() { _hasNull = false; _valueForNull = new[] { default(TValue) }; _valueComparer = EqualityComparer.Default; _keys = new ExtendedReadOnlyCollection(_dictionary.Keys); _values = new ExtendedReadOnlyCollection(_dictionary.Values); _readOnly = new ReadOnlyDictionary(this); } private void InitializeNullable() { _hasNull = false; _valueForNull = new[] { default(TValue) }; _valueComparer = EqualityComparer.Default; _keys = new ExtendedReadOnlyCollection( new EnumerationCollection( new ConditionalExtendedEnumerable( new[] { _typedNull }, _dictionary.Keys, () => _hasNull, null ) ) ); _values = new ExtendedReadOnlyCollection( new EnumerationCollection( new ConditionalExtendedEnumerable( _valueForNull, _dictionary.Values, () => _hasNull, null ) ) ); _readOnly = new ReadOnlyDictionary(this); } private void SetForNull(TValue value) { _valueForNull[0] = value; _hasNull = true; } private void TakeValueForNull(IDictionary dictionary) { if (dictionary.ContainsKey(_typedNull)) { _valueForNull = new[] { dictionary[_typedNull] }; _hasNull = true; } else { _valueForNull = new[] { default(TValue) }; _hasNull = false; } } } } #endif