1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
6 using System.Collections;
7 using System.Diagnostics;
8 using System.Runtime.CompilerServices;
9 using System.Runtime.Serialization;
11 namespace System.Collections.Generic
14 /// Used internally to control behavior of insertion into a <see cref="Dictionary{TKey, TValue}"/>.
16 internal enum InsertionBehavior : byte
19 /// The default insertion behavior.
24 /// Specifies that an existing entry with the same key should be overwritten if encountered.
26 OverwriteExisting = 1,
29 /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown.
34 [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))]
35 [DebuggerDisplay("Count = {Count}")]
37 [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
38 public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>, ISerializable, IDeserializationCallback
42 public int hashCode; // Lower 31 bits of hash code, -1 if unused
43 public int next; // Index of next entry, -1 if last
44 public TKey key; // Key of entry
45 public TValue value; // Value of entry
48 private int[] _buckets;
49 private Entry[] _entries;
51 private int _freeList;
52 private int _freeCount;
54 private IEqualityComparer<TKey> _comparer;
55 private KeyCollection _keys;
56 private ValueCollection _values;
57 private object _syncRoot;
59 // constants for serialization
60 private const string VersionName = "Version"; // Do not rename (binary serialization)
61 private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length
62 private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization)
63 private const string ComparerName = "Comparer"; // Do not rename (binary serialization)
65 public Dictionary() : this(0, null) { }
67 public Dictionary(int capacity) : this(capacity, null) { }
69 public Dictionary(IEqualityComparer<TKey> comparer) : this(0, comparer) { }
71 public Dictionary(int capacity, IEqualityComparer<TKey> comparer)
73 if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
74 if (capacity > 0) Initialize(capacity);
75 if (comparer != EqualityComparer<TKey>.Default)
80 if (typeof(TKey) == typeof(string) && _comparer == null)
82 // To start, move off default comparer for string which is randomised
83 _comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.Default;
87 public Dictionary(IDictionary<TKey, TValue> dictionary) : this(dictionary, null) { }
89 public Dictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) :
90 this(dictionary != null ? dictionary.Count : 0, comparer)
92 if (dictionary == null)
94 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
97 // It is likely that the passed-in dictionary is Dictionary<TKey,TValue>. When this is the case,
98 // avoid the enumerator allocation and overhead by looping through the entries array directly.
99 // We only do this when dictionary is Dictionary<TKey,TValue> and not a subclass, to maintain
100 // back-compat with subclasses that may have overridden the enumerator behavior.
101 if (dictionary.GetType() == typeof(Dictionary<TKey, TValue>))
103 Dictionary<TKey, TValue> d = (Dictionary<TKey, TValue>)dictionary;
104 int count = d._count;
105 Entry[] entries = d._entries;
106 for (int i = 0; i < count; i++)
108 if (entries[i].hashCode >= 0)
110 Add(entries[i].key, entries[i].value);
116 foreach (KeyValuePair<TKey, TValue> pair in dictionary)
118 Add(pair.Key, pair.Value);
122 public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : this(collection, null) { }
124 public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) :
125 this((collection as ICollection<KeyValuePair<TKey, TValue>>)?.Count ?? 0, comparer)
127 if (collection == null)
129 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
132 foreach (KeyValuePair<TKey, TValue> pair in collection)
134 Add(pair.Key, pair.Value);
138 protected Dictionary(SerializationInfo info, StreamingContext context)
140 // We can't do anything with the keys and values until the entire graph has been deserialized
141 // and we have a resonable estimate that GetHashCode is not going to fail. For the time being,
142 // we'll just cache this. The graph is not valid until OnDeserialization has been called.
143 HashHelpers.SerializationInfoTable.Add(this, info);
146 public IEqualityComparer<TKey> Comparer
150 return (_comparer == null || _comparer is NonRandomizedStringEqualityComparer) ? EqualityComparer<TKey>.Default : _comparer;
156 get { return _count - _freeCount; }
159 public KeyCollection Keys
163 if (_keys == null) _keys = new KeyCollection(this);
168 ICollection<TKey> IDictionary<TKey, TValue>.Keys
172 if (_keys == null) _keys = new KeyCollection(this);
177 IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
181 if (_keys == null) _keys = new KeyCollection(this);
186 public ValueCollection Values
190 if (_values == null) _values = new ValueCollection(this);
195 ICollection<TValue> IDictionary<TKey, TValue>.Values
199 if (_values == null) _values = new ValueCollection(this);
204 IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
208 if (_values == null) _values = new ValueCollection(this);
213 public TValue this[TKey key]
217 int i = FindEntry(key);
218 if (i >= 0) return _entries[i].value;
219 ThrowHelper.ThrowKeyNotFoundException(key);
220 return default(TValue);
224 bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting);
225 Debug.Assert(modified);
229 public void Add(TKey key, TValue value)
231 bool modified = TryInsert(key, value, InsertionBehavior.ThrowOnExisting);
232 Debug.Assert(modified); // If there was an existing key and the Add failed, an exception will already have been thrown.
235 void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair)
237 Add(keyValuePair.Key, keyValuePair.Value);
240 bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair)
242 int i = FindEntry(keyValuePair.Key);
243 if (i >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, keyValuePair.Value))
250 bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair)
252 int i = FindEntry(keyValuePair.Key);
253 if (i >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, keyValuePair.Value))
255 Remove(keyValuePair.Key);
266 Array.Clear(_buckets, 0, _buckets.Length);
272 Array.Clear(_entries, 0, count);
276 public bool ContainsKey(TKey key)
278 return FindEntry(key) >= 0;
281 public bool ContainsValue(TValue value)
285 for (int i = 0; i < _count; i++)
287 if (_entries[i].hashCode >= 0 && _entries[i].value == null) return true;
292 for (int i = 0; i < _count; i++)
294 if (_entries[i].hashCode >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, value)) return true;
300 private void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
304 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
307 if (index < 0 || index > array.Length)
309 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
312 if (array.Length - index < Count)
314 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
318 Entry[] entries = _entries;
319 for (int i = 0; i < count; i++)
321 if (entries[i].hashCode >= 0)
323 array[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
328 public Enumerator GetEnumerator()
330 return new Enumerator(this, Enumerator.KeyValuePair);
333 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
335 return new Enumerator(this, Enumerator.KeyValuePair);
338 public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
342 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info);
345 info.AddValue(VersionName, _version);
346 info.AddValue(ComparerName, _comparer ?? EqualityComparer<TKey>.Default, typeof(IEqualityComparer<TKey>));
347 info.AddValue(HashSizeName, _buckets == null ? 0 : _buckets.Length); // This is the length of the bucket array
349 if (_buckets != null)
351 var array = new KeyValuePair<TKey, TValue>[Count];
353 info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair<TKey, TValue>[]));
357 private int FindEntry(TKey key)
361 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
365 int[] buckets = _buckets;
366 Entry[] entries = _entries;
369 IEqualityComparer<TKey> comparer = _comparer;
370 if (comparer == null)
372 int hashCode = key.GetHashCode() & 0x7FFFFFFF;
373 // Value in _buckets is 1-based
374 i = buckets[hashCode % buckets.Length] - 1;
377 // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
378 // Test in if to drop range check for following array access
379 if ((uint)i >= (uint)entries.Length || (entries[i].hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entries[i].key, key)))
389 int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
390 // Value in _buckets is 1-based
391 i = buckets[hashCode % buckets.Length] - 1;
394 // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
395 // Test in if to drop range check for following array access
396 if ((uint)i >= (uint)entries.Length ||
397 (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)))
410 private int Initialize(int capacity)
412 int size = HashHelpers.GetPrime(capacity);
415 _buckets = new int[size];
416 _entries = new Entry[size];
421 private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
425 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
428 if (_buckets == null)
433 Entry[] entries = _entries;
434 IEqualityComparer<TKey> comparer = _comparer;
436 int hashCode = ((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key)) & 0x7FFFFFFF;
438 int collisionCount = 0;
439 ref int bucket = ref _buckets[hashCode % _buckets.Length];
440 // Value in _buckets is 1-based
443 if (comparer == null)
447 // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
448 // Test uint in if rather than loop condition to drop range check for following array access
449 if ((uint)i >= (uint)entries.Length)
454 if (entries[i].hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entries[i].key, key))
456 if (behavior == InsertionBehavior.OverwriteExisting)
458 entries[i].value = value;
463 if (behavior == InsertionBehavior.ThrowOnExisting)
465 ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
479 // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
480 // Test uint in if rather than loop condition to drop range check for following array access
481 if ((uint)i >= (uint)entries.Length)
486 if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
488 if (behavior == InsertionBehavior.OverwriteExisting)
490 entries[i].value = value;
495 if (behavior == InsertionBehavior.ThrowOnExisting)
497 ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
509 // Can be improved with "Ref Local Reassignment"
510 // https://github.com/dotnet/csharplang/blob/master/proposals/ref-local-reassignment.md
511 bool resized = false;
512 bool updateFreeList = false;
517 updateFreeList = true;
523 if (count == entries.Length)
533 ref int targetBucket = ref resized ? ref _buckets[hashCode % _buckets.Length] : ref bucket;
534 ref Entry entry = ref entries[index];
538 _freeList = entry.next;
540 entry.hashCode = hashCode;
541 // Value in _buckets is 1-based
542 entry.next = targetBucket - 1;
545 // Value in _buckets is 1-based
546 targetBucket = index + 1;
549 // Value types never rehash
550 if (default(TKey) == null && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer)
552 // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
553 // i.e. EqualityComparer<string>.Default.
555 Resize(entries.Length, true);
561 public virtual void OnDeserialization(object sender)
563 SerializationInfo siInfo;
564 HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
568 // We can return immediately if this function is called twice.
569 // Note we remove the serialization info from the table at the end of this method.
573 int realVersion = siInfo.GetInt32(VersionName);
574 int hashsize = siInfo.GetInt32(HashSizeName);
575 _comparer = (IEqualityComparer<TKey>)siInfo.GetValue(ComparerName, typeof(IEqualityComparer<TKey>));
579 Initialize(hashsize);
581 KeyValuePair<TKey, TValue>[] array = (KeyValuePair<TKey, TValue>[])
582 siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair<TKey, TValue>[]));
586 ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys);
589 for (int i = 0; i < array.Length; i++)
591 if (array[i].Key == null)
593 ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey);
595 Add(array[i].Key, array[i].Value);
603 _version = realVersion;
604 HashHelpers.SerializationInfoTable.Remove(this);
607 private void Resize()
609 Resize(HashHelpers.ExpandPrime(_count), false);
612 private void Resize(int newSize, bool forceNewHashCodes)
614 // Value types never rehash
615 Debug.Assert(!forceNewHashCodes || default(TKey) == null);
616 Debug.Assert(newSize >= _entries.Length);
618 int[] buckets = new int[newSize];
619 Entry[] entries = new Entry[newSize];
622 Array.Copy(_entries, 0, entries, 0, count);
624 if (default(TKey) == null && forceNewHashCodes)
626 for (int i = 0; i < count; i++)
628 if (entries[i].hashCode >= 0)
630 Debug.Assert(_comparer == null);
631 entries[i].hashCode = (entries[i].key.GetHashCode() & 0x7FFFFFFF);
636 for (int i = 0; i < count; i++)
638 if (entries[i].hashCode >= 0)
640 int bucket = entries[i].hashCode % newSize;
641 // Value in _buckets is 1-based
642 entries[i].next = buckets[bucket] - 1;
643 // Value in _buckets is 1-based
644 buckets[bucket] = i + 1;
652 // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional
653 // statement to copy the value for entry being removed into the output parameter.
654 // Code has been intentionally duplicated for performance reasons.
655 public bool Remove(TKey key)
659 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
662 if (_buckets != null)
664 int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
665 int bucket = hashCode % _buckets.Length;
667 // Value in _buckets is 1-based
668 int i = _buckets[bucket] - 1;
671 ref Entry entry = ref _entries[i];
673 if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer<TKey>.Default.Equals(entry.key, key)))
677 // Value in _buckets is 1-based
678 _buckets[bucket] = entry.next + 1;
682 _entries[last].next = entry.next;
685 entry.next = _freeList;
687 if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
689 entry.key = default(TKey);
691 if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
693 entry.value = default(TValue);
708 // This overload is a copy of the overload Remove(TKey key) with one additional
709 // statement to copy the value for entry being removed into the output parameter.
710 // Code has been intentionally duplicated for performance reasons.
711 public bool Remove(TKey key, out TValue value)
715 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
718 if (_buckets != null)
720 int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
721 int bucket = hashCode % _buckets.Length;
723 // Value in _buckets is 1-based
724 int i = _buckets[bucket] - 1;
727 ref Entry entry = ref _entries[i];
729 if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer<TKey>.Default.Equals(entry.key, key)))
733 // Value in _buckets is 1-based
734 _buckets[bucket] = entry.next + 1;
738 _entries[last].next = entry.next;
744 entry.next = _freeList;
746 if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
748 entry.key = default(TKey);
750 if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
752 entry.value = default(TValue);
764 value = default(TValue);
768 public bool TryGetValue(TKey key, out TValue value)
770 int i = FindEntry(key);
773 value = _entries[i].value;
776 value = default(TValue);
780 public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None);
782 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
784 get { return false; }
787 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
789 CopyTo(array, index);
792 void ICollection.CopyTo(Array array, int index)
796 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
801 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
804 if (array.GetLowerBound(0) != 0)
806 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
809 if (index < 0 || index > array.Length)
811 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
814 if (array.Length - index < Count)
816 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
819 KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[];
822 CopyTo(pairs, index);
824 else if (array is DictionaryEntry[])
826 DictionaryEntry[] dictEntryArray = array as DictionaryEntry[];
827 Entry[] entries = _entries;
828 for (int i = 0; i < _count; i++)
830 if (entries[i].hashCode >= 0)
832 dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
838 object[] objects = array as object[];
841 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
847 Entry[] entries = _entries;
848 for (int i = 0; i < count; i++)
850 if (entries[i].hashCode >= 0)
852 objects[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
856 catch (ArrayTypeMismatchException)
858 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
863 IEnumerator IEnumerable.GetEnumerator()
865 return new Enumerator(this, Enumerator.KeyValuePair);
869 /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
871 public int EnsureCapacity(int capacity)
874 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
875 int currentCapacity = _entries == null ? 0 : _entries.Length;
876 if (currentCapacity >= capacity)
877 return currentCapacity;
878 if (_buckets == null)
879 return Initialize(capacity);
880 int newSize = HashHelpers.GetPrime(capacity);
881 Resize(newSize, forceNewHashCodes: false);
886 /// Sets the capacity of this dictionary to what it would be if it had been originally initialized with all its entries
888 /// This method can be used to minimize the memory overhead
889 /// once it is known that no new elements will be added.
891 /// To allocate minimum size storage array, execute the following statements:
893 /// dictionary.Clear();
894 /// dictionary.TrimExcess();
896 public void TrimExcess()
902 /// Sets the capacity of this dictionary to hold up 'capacity' entries without any further expansion of its backing storage
904 /// This method can be used to minimize the memory overhead
905 /// once it is known that no new elements will be added.
907 public void TrimExcess(int capacity)
909 if (capacity < Count)
910 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
911 int newSize = HashHelpers.GetPrime(capacity);
913 Entry[] oldEntries = _entries;
914 int currentCapacity = oldEntries == null ? 0 : oldEntries.Length;
915 if (newSize >= currentCapacity)
918 int oldCount = _count;
920 Entry[] entries = _entries;
921 int[] buckets = _buckets;
923 for (int i = 0; i < oldCount; i++)
925 int hashCode = oldEntries[i].hashCode;
928 ref Entry entry = ref entries[count];
929 entry = oldEntries[i];
930 int bucket = hashCode % newSize;
931 // Value in _buckets is 1-based
932 entry.next = buckets[bucket] - 1;
933 // Value in _buckets is 1-based
934 buckets[bucket] = count + 1;
942 bool ICollection.IsSynchronized
944 get { return false; }
947 object ICollection.SyncRoot
951 if (_syncRoot == null)
953 System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
959 bool IDictionary.IsFixedSize
961 get { return false; }
964 bool IDictionary.IsReadOnly
966 get { return false; }
969 ICollection IDictionary.Keys
971 get { return (ICollection)Keys; }
974 ICollection IDictionary.Values
976 get { return (ICollection)Values; }
979 object IDictionary.this[object key]
983 if (IsCompatibleKey(key))
985 int i = FindEntry((TKey)key);
988 return _entries[i].value;
997 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
999 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<TValue>(value, ExceptionArgument.value);
1003 TKey tempKey = (TKey)key;
1006 this[tempKey] = (TValue)value;
1008 catch (InvalidCastException)
1010 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue));
1013 catch (InvalidCastException)
1015 ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey));
1020 private static bool IsCompatibleKey(object key)
1024 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
1026 return (key is TKey);
1029 void IDictionary.Add(object key, object value)
1033 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
1035 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<TValue>(value, ExceptionArgument.value);
1039 TKey tempKey = (TKey)key;
1043 Add(tempKey, (TValue)value);
1045 catch (InvalidCastException)
1047 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue));
1050 catch (InvalidCastException)
1052 ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey));
1056 bool IDictionary.Contains(object key)
1058 if (IsCompatibleKey(key))
1060 return ContainsKey((TKey)key);
1066 IDictionaryEnumerator IDictionary.GetEnumerator()
1068 return new Enumerator(this, Enumerator.DictEntry);
1071 void IDictionary.Remove(object key)
1073 if (IsCompatibleKey(key))
1079 public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>,
1080 IDictionaryEnumerator
1082 private Dictionary<TKey, TValue> _dictionary;
1083 private int _version;
1085 private KeyValuePair<TKey, TValue> _current;
1086 private int _getEnumeratorRetType; // What should Enumerator.Current return?
1088 internal const int DictEntry = 1;
1089 internal const int KeyValuePair = 2;
1091 internal Enumerator(Dictionary<TKey, TValue> dictionary, int getEnumeratorRetType)
1093 _dictionary = dictionary;
1094 _version = dictionary._version;
1096 _getEnumeratorRetType = getEnumeratorRetType;
1097 _current = new KeyValuePair<TKey, TValue>();
1100 public bool MoveNext()
1102 if (_version != _dictionary._version)
1104 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1107 // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
1108 // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
1109 while ((uint)_index < (uint)_dictionary._count)
1111 ref Entry entry = ref _dictionary._entries[_index++];
1113 if (entry.hashCode >= 0)
1115 _current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
1120 _index = _dictionary._count + 1;
1121 _current = new KeyValuePair<TKey, TValue>();
1125 public KeyValuePair<TKey, TValue> Current
1127 get { return _current; }
1130 public void Dispose()
1134 object IEnumerator.Current
1138 if (_index == 0 || (_index == _dictionary._count + 1))
1140 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1143 if (_getEnumeratorRetType == DictEntry)
1145 return new System.Collections.DictionaryEntry(_current.Key, _current.Value);
1149 return new KeyValuePair<TKey, TValue>(_current.Key, _current.Value);
1154 void IEnumerator.Reset()
1156 if (_version != _dictionary._version)
1158 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1162 _current = new KeyValuePair<TKey, TValue>();
1165 DictionaryEntry IDictionaryEnumerator.Entry
1169 if (_index == 0 || (_index == _dictionary._count + 1))
1171 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1174 return new DictionaryEntry(_current.Key, _current.Value);
1178 object IDictionaryEnumerator.Key
1182 if (_index == 0 || (_index == _dictionary._count + 1))
1184 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1187 return _current.Key;
1191 object IDictionaryEnumerator.Value
1195 if (_index == 0 || (_index == _dictionary._count + 1))
1197 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1200 return _current.Value;
1205 [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))]
1206 [DebuggerDisplay("Count = {Count}")]
1207 public sealed class KeyCollection : ICollection<TKey>, ICollection, IReadOnlyCollection<TKey>
1209 private Dictionary<TKey, TValue> _dictionary;
1211 public KeyCollection(Dictionary<TKey, TValue> dictionary)
1213 if (dictionary == null)
1215 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
1217 _dictionary = dictionary;
1220 public Enumerator GetEnumerator()
1222 return new Enumerator(_dictionary);
1225 public void CopyTo(TKey[] array, int index)
1229 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1232 if (index < 0 || index > array.Length)
1234 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1237 if (array.Length - index < _dictionary.Count)
1239 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1242 int count = _dictionary._count;
1243 Entry[] entries = _dictionary._entries;
1244 for (int i = 0; i < count; i++)
1246 if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
1252 get { return _dictionary.Count; }
1255 bool ICollection<TKey>.IsReadOnly
1257 get { return true; }
1260 void ICollection<TKey>.Add(TKey item)
1262 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
1265 void ICollection<TKey>.Clear()
1267 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
1270 bool ICollection<TKey>.Contains(TKey item)
1272 return _dictionary.ContainsKey(item);
1275 bool ICollection<TKey>.Remove(TKey item)
1277 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
1281 IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator()
1283 return new Enumerator(_dictionary);
1286 IEnumerator IEnumerable.GetEnumerator()
1288 return new Enumerator(_dictionary);
1291 void ICollection.CopyTo(Array array, int index)
1295 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1298 if (array.Rank != 1)
1300 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
1303 if (array.GetLowerBound(0) != 0)
1305 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
1308 if (index < 0 || index > array.Length)
1310 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1313 if (array.Length - index < _dictionary.Count)
1315 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1318 TKey[] keys = array as TKey[];
1321 CopyTo(keys, index);
1325 object[] objects = array as object[];
1326 if (objects == null)
1328 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1331 int count = _dictionary._count;
1332 Entry[] entries = _dictionary._entries;
1335 for (int i = 0; i < count; i++)
1337 if (entries[i].hashCode >= 0) objects[index++] = entries[i].key;
1340 catch (ArrayTypeMismatchException)
1342 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1347 bool ICollection.IsSynchronized
1349 get { return false; }
1352 object ICollection.SyncRoot
1354 get { return ((ICollection)_dictionary).SyncRoot; }
1357 public struct Enumerator : IEnumerator<TKey>, System.Collections.IEnumerator
1359 private Dictionary<TKey, TValue> _dictionary;
1361 private int _version;
1362 private TKey _currentKey;
1364 internal Enumerator(Dictionary<TKey, TValue> dictionary)
1366 _dictionary = dictionary;
1367 _version = dictionary._version;
1369 _currentKey = default(TKey);
1372 public void Dispose()
1376 public bool MoveNext()
1378 if (_version != _dictionary._version)
1380 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1383 while ((uint)_index < (uint)_dictionary._count)
1385 ref Entry entry = ref _dictionary._entries[_index++];
1387 if (entry.hashCode >= 0)
1389 _currentKey = entry.key;
1394 _index = _dictionary._count + 1;
1395 _currentKey = default(TKey);
1407 object System.Collections.IEnumerator.Current
1411 if (_index == 0 || (_index == _dictionary._count + 1))
1413 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1420 void System.Collections.IEnumerator.Reset()
1422 if (_version != _dictionary._version)
1424 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1428 _currentKey = default(TKey);
1433 [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))]
1434 [DebuggerDisplay("Count = {Count}")]
1435 public sealed class ValueCollection : ICollection<TValue>, ICollection, IReadOnlyCollection<TValue>
1437 private Dictionary<TKey, TValue> _dictionary;
1439 public ValueCollection(Dictionary<TKey, TValue> dictionary)
1441 if (dictionary == null)
1443 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
1445 _dictionary = dictionary;
1448 public Enumerator GetEnumerator()
1450 return new Enumerator(_dictionary);
1453 public void CopyTo(TValue[] array, int index)
1457 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1460 if (index < 0 || index > array.Length)
1462 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1465 if (array.Length - index < _dictionary.Count)
1467 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1470 int count = _dictionary._count;
1471 Entry[] entries = _dictionary._entries;
1472 for (int i = 0; i < count; i++)
1474 if (entries[i].hashCode >= 0) array[index++] = entries[i].value;
1480 get { return _dictionary.Count; }
1483 bool ICollection<TValue>.IsReadOnly
1485 get { return true; }
1488 void ICollection<TValue>.Add(TValue item)
1490 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
1493 bool ICollection<TValue>.Remove(TValue item)
1495 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
1499 void ICollection<TValue>.Clear()
1501 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
1504 bool ICollection<TValue>.Contains(TValue item)
1506 return _dictionary.ContainsValue(item);
1509 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
1511 return new Enumerator(_dictionary);
1514 IEnumerator IEnumerable.GetEnumerator()
1516 return new Enumerator(_dictionary);
1519 void ICollection.CopyTo(Array array, int index)
1523 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1526 if (array.Rank != 1)
1528 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
1531 if (array.GetLowerBound(0) != 0)
1533 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
1536 if (index < 0 || index > array.Length)
1538 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1541 if (array.Length - index < _dictionary.Count)
1542 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1544 TValue[] values = array as TValue[];
1547 CopyTo(values, index);
1551 object[] objects = array as object[];
1552 if (objects == null)
1554 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1557 int count = _dictionary._count;
1558 Entry[] entries = _dictionary._entries;
1561 for (int i = 0; i < count; i++)
1563 if (entries[i].hashCode >= 0) objects[index++] = entries[i].value;
1566 catch (ArrayTypeMismatchException)
1568 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1573 bool ICollection.IsSynchronized
1575 get { return false; }
1578 object ICollection.SyncRoot
1580 get { return ((ICollection)_dictionary).SyncRoot; }
1583 public struct Enumerator : IEnumerator<TValue>, System.Collections.IEnumerator
1585 private Dictionary<TKey, TValue> _dictionary;
1587 private int _version;
1588 private TValue _currentValue;
1590 internal Enumerator(Dictionary<TKey, TValue> dictionary)
1592 _dictionary = dictionary;
1593 _version = dictionary._version;
1595 _currentValue = default(TValue);
1598 public void Dispose()
1602 public bool MoveNext()
1604 if (_version != _dictionary._version)
1606 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1609 while ((uint)_index < (uint)_dictionary._count)
1611 ref Entry entry = ref _dictionary._entries[_index++];
1613 if (entry.hashCode >= 0)
1615 _currentValue = entry.value;
1619 _index = _dictionary._count + 1;
1620 _currentValue = default(TValue);
1624 public TValue Current
1628 return _currentValue;
1632 object System.Collections.IEnumerator.Current
1636 if (_index == 0 || (_index == _dictionary._count + 1))
1638 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1641 return _currentValue;
1645 void System.Collections.IEnumerator.Reset()
1647 if (_version != _dictionary._version)
1649 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1652 _currentValue = default(TValue);