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 _comparer = comparer ?? EqualityComparer<TKey>.Default;
77 if (_comparer == EqualityComparer<string>.Default)
79 _comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.Default;
83 public Dictionary(IDictionary<TKey, TValue> dictionary) : this(dictionary, null) { }
85 public Dictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) :
86 this(dictionary != null ? dictionary.Count : 0, comparer)
88 if (dictionary == null)
90 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
93 // It is likely that the passed-in dictionary is Dictionary<TKey,TValue>. When this is the case,
94 // avoid the enumerator allocation and overhead by looping through the entries array directly.
95 // We only do this when dictionary is Dictionary<TKey,TValue> and not a subclass, to maintain
96 // back-compat with subclasses that may have overridden the enumerator behavior.
97 if (dictionary.GetType() == typeof(Dictionary<TKey, TValue>))
99 Dictionary<TKey, TValue> d = (Dictionary<TKey, TValue>)dictionary;
100 int count = d._count;
101 Entry[] entries = d._entries;
102 for (int i = 0; i < count; i++)
104 if (entries[i].hashCode >= 0)
106 Add(entries[i].key, entries[i].value);
112 foreach (KeyValuePair<TKey, TValue> pair in dictionary)
114 Add(pair.Key, pair.Value);
118 public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : this(collection, null) { }
120 public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) :
121 this((collection as ICollection<KeyValuePair<TKey, TValue>>)?.Count ?? 0, comparer)
123 if (collection == null)
125 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
128 foreach (KeyValuePair<TKey, TValue> pair in collection)
130 Add(pair.Key, pair.Value);
134 protected Dictionary(SerializationInfo info, StreamingContext context)
136 // We can't do anything with the keys and values until the entire graph has been deserialized
137 // and we have a resonable estimate that GetHashCode is not going to fail. For the time being,
138 // we'll just cache this. The graph is not valid until OnDeserialization has been called.
139 HashHelpers.SerializationInfoTable.Add(this, info);
142 public IEqualityComparer<TKey> Comparer
152 get { return _count - _freeCount; }
155 public KeyCollection Keys
159 if (_keys == null) _keys = new KeyCollection(this);
164 ICollection<TKey> IDictionary<TKey, TValue>.Keys
168 if (_keys == null) _keys = new KeyCollection(this);
173 IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
177 if (_keys == null) _keys = new KeyCollection(this);
182 public ValueCollection Values
186 if (_values == null) _values = new ValueCollection(this);
191 ICollection<TValue> IDictionary<TKey, TValue>.Values
195 if (_values == null) _values = new ValueCollection(this);
200 IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
204 if (_values == null) _values = new ValueCollection(this);
209 public TValue this[TKey key]
213 int i = FindEntry(key);
214 if (i >= 0) return _entries[i].value;
215 ThrowHelper.ThrowKeyNotFoundException(key);
216 return default(TValue);
220 bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting);
221 Debug.Assert(modified);
225 public void Add(TKey key, TValue value)
227 bool modified = TryInsert(key, value, InsertionBehavior.ThrowOnExisting);
228 Debug.Assert(modified); // If there was an existing key and the Add failed, an exception will already have been thrown.
231 void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair)
233 Add(keyValuePair.Key, keyValuePair.Value);
236 bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair)
238 int i = FindEntry(keyValuePair.Key);
239 if (i >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, keyValuePair.Value))
246 bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair)
248 int i = FindEntry(keyValuePair.Key);
249 if (i >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, keyValuePair.Value))
251 Remove(keyValuePair.Key);
262 int[] buckets = _buckets;
263 for (int i = 0; i < buckets.Length; i++)
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 EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;
293 for (int i = 0; i < _count; i++)
295 if (_entries[i].hashCode >= 0 && c.Equals(_entries[i].value, value)) return true;
301 private void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
305 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
308 if (index < 0 || index > array.Length)
310 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
313 if (array.Length - index < Count)
315 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
319 Entry[] entries = _entries;
320 for (int i = 0; i < count; i++)
322 if (entries[i].hashCode >= 0)
324 array[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
329 public Enumerator GetEnumerator()
331 return new Enumerator(this, Enumerator.KeyValuePair);
334 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
336 return new Enumerator(this, Enumerator.KeyValuePair);
339 public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
343 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info);
346 info.AddValue(VersionName, _version);
347 info.AddValue(ComparerName, _comparer, typeof(IEqualityComparer<TKey>));
348 info.AddValue(HashSizeName, _buckets == null ? 0 : _buckets.Length); // This is the length of the bucket array
350 if (_buckets != null)
352 var array = new KeyValuePair<TKey, TValue>[Count];
354 info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair<TKey, TValue>[]));
358 private int FindEntry(TKey key)
362 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
365 if (_buckets != null)
367 int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
368 for (int i = _buckets[hashCode % _buckets.Length]; i >= 0; i = _entries[i].next)
370 if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) return i;
376 private int Initialize(int capacity)
378 int size = HashHelpers.GetPrime(capacity);
379 int[] buckets = new int[size];
380 for (int i = 0; i < buckets.Length; i++)
387 _entries = new Entry[size];
392 private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
396 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
399 if (_buckets == null) Initialize(0);
400 int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
401 int targetBucket = hashCode % _buckets.Length;
402 int collisionCount = 0;
404 for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
406 if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key))
408 if (behavior == InsertionBehavior.OverwriteExisting)
410 _entries[i].value = value;
415 if (behavior == InsertionBehavior.ThrowOnExisting)
417 ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
429 _freeList = _entries[index].next;
434 if (_count == _entries.Length)
437 targetBucket = hashCode % _buckets.Length;
443 _entries[index].hashCode = hashCode;
444 _entries[index].next = _buckets[targetBucket];
445 _entries[index].key = key;
446 _entries[index].value = value;
447 _buckets[targetBucket] = index;
450 // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
451 // i.e. EqualityComparer<string>.Default.
453 if (collisionCount > HashHelpers.HashCollisionThreshold && _comparer is NonRandomizedStringEqualityComparer)
455 _comparer = (IEqualityComparer<TKey>)EqualityComparer<string>.Default;
456 Resize(_entries.Length, true);
462 public virtual void OnDeserialization(object sender)
464 SerializationInfo siInfo;
465 HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
469 // We can return immediately if this function is called twice.
470 // Note we remove the serialization info from the table at the end of this method.
474 int realVersion = siInfo.GetInt32(VersionName);
475 int hashsize = siInfo.GetInt32(HashSizeName);
476 _comparer = (IEqualityComparer<TKey>)siInfo.GetValue(ComparerName, typeof(IEqualityComparer<TKey>));
480 Initialize(hashsize);
482 KeyValuePair<TKey, TValue>[] array = (KeyValuePair<TKey, TValue>[])
483 siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair<TKey, TValue>[]));
487 ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys);
490 for (int i = 0; i < array.Length; i++)
492 if (array[i].Key == null)
494 ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey);
496 Add(array[i].Key, array[i].Value);
504 _version = realVersion;
505 HashHelpers.SerializationInfoTable.Remove(this);
508 private void Resize()
510 Resize(HashHelpers.ExpandPrime(_count), false);
513 private void Resize(int newSize, bool forceNewHashCodes)
515 Debug.Assert(newSize >= _entries.Length);
517 int[] buckets = new int[newSize];
518 for (int i = 0; i < buckets.Length; i++)
522 Entry[] entries = new Entry[newSize];
525 Array.Copy(_entries, 0, entries, 0, count);
527 if (forceNewHashCodes)
529 for (int i = 0; i < count; i++)
531 if (entries[i].hashCode != -1)
533 entries[i].hashCode = (_comparer.GetHashCode(entries[i].key) & 0x7FFFFFFF);
538 for (int i = 0; i < count; i++)
540 if (entries[i].hashCode >= 0)
542 int bucket = entries[i].hashCode % newSize;
543 entries[i].next = buckets[bucket];
552 // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional
553 // statement to copy the value for entry being removed into the output parameter.
554 // Code has been intentionally duplicated for performance reasons.
555 public bool Remove(TKey key)
559 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
562 if (_buckets != null)
564 int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
565 int bucket = hashCode % _buckets.Length;
567 int i = _buckets[bucket];
570 ref Entry entry = ref _entries[i];
572 if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key))
576 _buckets[bucket] = entry.next;
580 _entries[last].next = entry.next;
583 entry.next = _freeList;
585 if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
587 entry.key = default(TKey);
589 if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
591 entry.value = default(TValue);
606 // This overload is a copy of the overload Remove(TKey key) with one additional
607 // statement to copy the value for entry being removed into the output parameter.
608 // Code has been intentionally duplicated for performance reasons.
609 public bool Remove(TKey key, out TValue value)
613 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
616 if (_buckets != null)
618 int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
619 int bucket = hashCode % _buckets.Length;
621 int i = _buckets[bucket];
624 ref Entry entry = ref _entries[i];
626 if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key))
630 _buckets[bucket] = entry.next;
634 _entries[last].next = entry.next;
640 entry.next = _freeList;
642 if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
644 entry.key = default(TKey);
646 if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
648 entry.value = default(TValue);
660 value = default(TValue);
664 public bool TryGetValue(TKey key, out TValue value)
666 int i = FindEntry(key);
669 value = _entries[i].value;
672 value = default(TValue);
676 public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None);
678 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
680 get { return false; }
683 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
685 CopyTo(array, index);
688 void ICollection.CopyTo(Array array, int index)
692 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
697 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
700 if (array.GetLowerBound(0) != 0)
702 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
705 if (index < 0 || index > array.Length)
707 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
710 if (array.Length - index < Count)
712 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
715 KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[];
718 CopyTo(pairs, index);
720 else if (array is DictionaryEntry[])
722 DictionaryEntry[] dictEntryArray = array as DictionaryEntry[];
723 Entry[] entries = _entries;
724 for (int i = 0; i < _count; i++)
726 if (entries[i].hashCode >= 0)
728 dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
734 object[] objects = array as object[];
737 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
743 Entry[] entries = _entries;
744 for (int i = 0; i < count; i++)
746 if (entries[i].hashCode >= 0)
748 objects[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
752 catch (ArrayTypeMismatchException)
754 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
759 IEnumerator IEnumerable.GetEnumerator()
761 return new Enumerator(this, Enumerator.KeyValuePair);
765 /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
767 public int EnsureCapacity(int capacity)
770 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
771 int currentCapacity = _entries == null ? 0 : _entries.Length;
772 if (currentCapacity >= capacity)
773 return currentCapacity;
774 if (_buckets == null)
775 return Initialize(capacity);
776 int newSize = HashHelpers.GetPrime(capacity);
777 Resize(newSize, forceNewHashCodes: false);
782 /// Sets the capacity of this dictionary to what it would be if it had been originally initialized with all its entries
784 /// This method can be used to minimize the memory overhead
785 /// once it is known that no new elements will be added.
787 /// To allocate minimum size storage array, execute the following statements:
789 /// dictionary.Clear();
790 /// dictionary.TrimExcess();
792 public void TrimExcess()
798 /// Sets the capacity of this dictionary to hold up 'capacity' entries without any further expansion of its backing storage
800 /// This method can be used to minimize the memory overhead
801 /// once it is known that no new elements will be added.
803 public void TrimExcess(int capacity)
805 if (capacity < Count)
806 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
807 int newSize = HashHelpers.GetPrime(capacity);
809 Entry[] oldEntries = _entries;
810 int currentCapacity = oldEntries == null ? 0 : oldEntries.Length;
811 if (newSize >= currentCapacity)
814 int oldCount = _count;
816 Entry[] entries = _entries;
817 int[] buckets = _buckets;
819 for (int i = 0; i < oldCount; i++)
821 int hashCode = oldEntries[i].hashCode;
824 ref Entry entry = ref entries[count];
825 entry = oldEntries[i];
826 int bucket = hashCode % newSize;
827 entry.next = buckets[bucket];
828 buckets[bucket] = count;
836 bool ICollection.IsSynchronized
838 get { return false; }
841 object ICollection.SyncRoot
845 if (_syncRoot == null)
847 System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
853 bool IDictionary.IsFixedSize
855 get { return false; }
858 bool IDictionary.IsReadOnly
860 get { return false; }
863 ICollection IDictionary.Keys
865 get { return (ICollection)Keys; }
868 ICollection IDictionary.Values
870 get { return (ICollection)Values; }
873 object IDictionary.this[object key]
877 if (IsCompatibleKey(key))
879 int i = FindEntry((TKey)key);
882 return _entries[i].value;
891 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
893 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<TValue>(value, ExceptionArgument.value);
897 TKey tempKey = (TKey)key;
900 this[tempKey] = (TValue)value;
902 catch (InvalidCastException)
904 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue));
907 catch (InvalidCastException)
909 ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey));
914 private static bool IsCompatibleKey(object key)
918 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
920 return (key is TKey);
923 void IDictionary.Add(object key, object value)
927 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
929 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<TValue>(value, ExceptionArgument.value);
933 TKey tempKey = (TKey)key;
937 Add(tempKey, (TValue)value);
939 catch (InvalidCastException)
941 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue));
944 catch (InvalidCastException)
946 ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey));
950 bool IDictionary.Contains(object key)
952 if (IsCompatibleKey(key))
954 return ContainsKey((TKey)key);
960 IDictionaryEnumerator IDictionary.GetEnumerator()
962 return new Enumerator(this, Enumerator.DictEntry);
965 void IDictionary.Remove(object key)
967 if (IsCompatibleKey(key))
973 public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>,
974 IDictionaryEnumerator
976 private Dictionary<TKey, TValue> _dictionary;
977 private int _version;
979 private KeyValuePair<TKey, TValue> _current;
980 private int _getEnumeratorRetType; // What should Enumerator.Current return?
982 internal const int DictEntry = 1;
983 internal const int KeyValuePair = 2;
985 internal Enumerator(Dictionary<TKey, TValue> dictionary, int getEnumeratorRetType)
987 _dictionary = dictionary;
988 _version = dictionary._version;
990 _getEnumeratorRetType = getEnumeratorRetType;
991 _current = new KeyValuePair<TKey, TValue>();
994 public bool MoveNext()
996 if (_version != _dictionary._version)
998 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1001 // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
1002 // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
1003 while ((uint)_index < (uint)_dictionary._count)
1005 ref Entry entry = ref _dictionary._entries[_index++];
1007 if (entry.hashCode >= 0)
1009 _current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
1014 _index = _dictionary._count + 1;
1015 _current = new KeyValuePair<TKey, TValue>();
1019 public KeyValuePair<TKey, TValue> Current
1021 get { return _current; }
1024 public void Dispose()
1028 object IEnumerator.Current
1032 if (_index == 0 || (_index == _dictionary._count + 1))
1034 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1037 if (_getEnumeratorRetType == DictEntry)
1039 return new System.Collections.DictionaryEntry(_current.Key, _current.Value);
1043 return new KeyValuePair<TKey, TValue>(_current.Key, _current.Value);
1048 void IEnumerator.Reset()
1050 if (_version != _dictionary._version)
1052 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1056 _current = new KeyValuePair<TKey, TValue>();
1059 DictionaryEntry IDictionaryEnumerator.Entry
1063 if (_index == 0 || (_index == _dictionary._count + 1))
1065 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1068 return new DictionaryEntry(_current.Key, _current.Value);
1072 object IDictionaryEnumerator.Key
1076 if (_index == 0 || (_index == _dictionary._count + 1))
1078 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1081 return _current.Key;
1085 object IDictionaryEnumerator.Value
1089 if (_index == 0 || (_index == _dictionary._count + 1))
1091 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1094 return _current.Value;
1099 [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))]
1100 [DebuggerDisplay("Count = {Count}")]
1101 public sealed class KeyCollection : ICollection<TKey>, ICollection, IReadOnlyCollection<TKey>
1103 private Dictionary<TKey, TValue> _dictionary;
1105 public KeyCollection(Dictionary<TKey, TValue> dictionary)
1107 if (dictionary == null)
1109 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
1111 _dictionary = dictionary;
1114 public Enumerator GetEnumerator()
1116 return new Enumerator(_dictionary);
1119 public void CopyTo(TKey[] array, int index)
1123 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1126 if (index < 0 || index > array.Length)
1128 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1131 if (array.Length - index < _dictionary.Count)
1133 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1136 int count = _dictionary._count;
1137 Entry[] entries = _dictionary._entries;
1138 for (int i = 0; i < count; i++)
1140 if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
1146 get { return _dictionary.Count; }
1149 bool ICollection<TKey>.IsReadOnly
1151 get { return true; }
1154 void ICollection<TKey>.Add(TKey item)
1156 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
1159 void ICollection<TKey>.Clear()
1161 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
1164 bool ICollection<TKey>.Contains(TKey item)
1166 return _dictionary.ContainsKey(item);
1169 bool ICollection<TKey>.Remove(TKey item)
1171 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
1175 IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator()
1177 return new Enumerator(_dictionary);
1180 IEnumerator IEnumerable.GetEnumerator()
1182 return new Enumerator(_dictionary);
1185 void ICollection.CopyTo(Array array, int index)
1189 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1192 if (array.Rank != 1)
1194 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
1197 if (array.GetLowerBound(0) != 0)
1199 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
1202 if (index < 0 || index > array.Length)
1204 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1207 if (array.Length - index < _dictionary.Count)
1209 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1212 TKey[] keys = array as TKey[];
1215 CopyTo(keys, index);
1219 object[] objects = array as object[];
1220 if (objects == null)
1222 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1225 int count = _dictionary._count;
1226 Entry[] entries = _dictionary._entries;
1229 for (int i = 0; i < count; i++)
1231 if (entries[i].hashCode >= 0) objects[index++] = entries[i].key;
1234 catch (ArrayTypeMismatchException)
1236 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1241 bool ICollection.IsSynchronized
1243 get { return false; }
1246 object ICollection.SyncRoot
1248 get { return ((ICollection)_dictionary).SyncRoot; }
1251 public struct Enumerator : IEnumerator<TKey>, System.Collections.IEnumerator
1253 private Dictionary<TKey, TValue> _dictionary;
1255 private int _version;
1256 private TKey _currentKey;
1258 internal Enumerator(Dictionary<TKey, TValue> dictionary)
1260 _dictionary = dictionary;
1261 _version = dictionary._version;
1263 _currentKey = default(TKey);
1266 public void Dispose()
1270 public bool MoveNext()
1272 if (_version != _dictionary._version)
1274 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1277 while ((uint)_index < (uint)_dictionary._count)
1279 ref Entry entry = ref _dictionary._entries[_index++];
1281 if (entry.hashCode >= 0)
1283 _currentKey = entry.key;
1288 _index = _dictionary._count + 1;
1289 _currentKey = default(TKey);
1301 object System.Collections.IEnumerator.Current
1305 if (_index == 0 || (_index == _dictionary._count + 1))
1307 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1314 void System.Collections.IEnumerator.Reset()
1316 if (_version != _dictionary._version)
1318 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1322 _currentKey = default(TKey);
1327 [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))]
1328 [DebuggerDisplay("Count = {Count}")]
1329 public sealed class ValueCollection : ICollection<TValue>, ICollection, IReadOnlyCollection<TValue>
1331 private Dictionary<TKey, TValue> _dictionary;
1333 public ValueCollection(Dictionary<TKey, TValue> dictionary)
1335 if (dictionary == null)
1337 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
1339 _dictionary = dictionary;
1342 public Enumerator GetEnumerator()
1344 return new Enumerator(_dictionary);
1347 public void CopyTo(TValue[] array, int index)
1351 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1354 if (index < 0 || index > array.Length)
1356 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1359 if (array.Length - index < _dictionary.Count)
1361 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1364 int count = _dictionary._count;
1365 Entry[] entries = _dictionary._entries;
1366 for (int i = 0; i < count; i++)
1368 if (entries[i].hashCode >= 0) array[index++] = entries[i].value;
1374 get { return _dictionary.Count; }
1377 bool ICollection<TValue>.IsReadOnly
1379 get { return true; }
1382 void ICollection<TValue>.Add(TValue item)
1384 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
1387 bool ICollection<TValue>.Remove(TValue item)
1389 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
1393 void ICollection<TValue>.Clear()
1395 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
1398 bool ICollection<TValue>.Contains(TValue item)
1400 return _dictionary.ContainsValue(item);
1403 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
1405 return new Enumerator(_dictionary);
1408 IEnumerator IEnumerable.GetEnumerator()
1410 return new Enumerator(_dictionary);
1413 void ICollection.CopyTo(Array array, int index)
1417 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1420 if (array.Rank != 1)
1422 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
1425 if (array.GetLowerBound(0) != 0)
1427 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
1430 if (index < 0 || index > array.Length)
1432 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1435 if (array.Length - index < _dictionary.Count)
1436 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
1438 TValue[] values = array as TValue[];
1441 CopyTo(values, index);
1445 object[] objects = array as object[];
1446 if (objects == null)
1448 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1451 int count = _dictionary._count;
1452 Entry[] entries = _dictionary._entries;
1455 for (int i = 0; i < count; i++)
1457 if (entries[i].hashCode >= 0) objects[index++] = entries[i].value;
1460 catch (ArrayTypeMismatchException)
1462 ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType();
1467 bool ICollection.IsSynchronized
1469 get { return false; }
1472 object ICollection.SyncRoot
1474 get { return ((ICollection)_dictionary).SyncRoot; }
1477 public struct Enumerator : IEnumerator<TValue>, System.Collections.IEnumerator
1479 private Dictionary<TKey, TValue> _dictionary;
1481 private int _version;
1482 private TValue _currentValue;
1484 internal Enumerator(Dictionary<TKey, TValue> dictionary)
1486 _dictionary = dictionary;
1487 _version = dictionary._version;
1489 _currentValue = default(TValue);
1492 public void Dispose()
1496 public bool MoveNext()
1498 if (_version != _dictionary._version)
1500 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1503 while ((uint)_index < (uint)_dictionary._count)
1505 ref Entry entry = ref _dictionary._entries[_index++];
1507 if (entry.hashCode >= 0)
1509 _currentValue = entry.value;
1513 _index = _dictionary._count + 1;
1514 _currentValue = default(TValue);
1518 public TValue Current
1522 return _currentValue;
1526 object System.Collections.IEnumerator.Current
1530 if (_index == 0 || (_index == _dictionary._count + 1))
1532 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
1535 return _currentValue;
1539 void System.Collections.IEnumerator.Reset()
1541 if (_version != _dictionary._version)
1543 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
1546 _currentValue = default(TValue);