2 // System.Collections.Generic.Dictionary
5 // Sureshkumar T (tsureshkumar@novell.com)
6 // Marek Safar (marek.safar@gmail.com)
7 // Ankit Jain (radical@corewars.org)
8 // David Waite (mass@akuma.org)
9 // Juraj Skripsky (js@hotfeet.ch)
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Copyright (C) 2005 David Waite
14 // Copyright (C) 2007 HotFeet GmbH (http://www.hotfeet.ch)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 using System.Collections;
39 using System.Collections.Generic;
40 using System.Runtime.Serialization;
41 using System.Security.Permissions;
42 using System.Runtime.InteropServices;
43 using System.Diagnostics;
45 namespace System.Collections.Generic {
48 * Declare this outside the main class so it doesn't have to be inflated for each
49 * instantiation of Dictionary.
51 internal struct Link {
58 [DebuggerDisplay ("Count={Count}")]
59 [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
60 public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,
63 ICollection<KeyValuePair<TKey, TValue>>,
64 IEnumerable<KeyValuePair<TKey, TValue>>,
66 IDeserializationCallback
68 // The implementation of this class uses a hash table and linked lists
69 // (see: http://msdn2.microsoft.com/en-us/library/ms379571(VS.80).aspx).
71 // We use a kind of "mini-heap" instead of reference-based linked lists:
72 // "keySlots" and "valueSlots" is the heap itself, it stores the data
73 // "linkSlots" contains information about how the slots in the heap
74 // are connected into linked lists
75 // In addition, the HashCode field can be used to check if the
76 // corresponding key and value are present (HashCode has the
77 // HASH_FLAG bit set in this case), so, to iterate over all the
78 // items in the dictionary, simply iterate the linkSlots array
79 // and check for the HASH_FLAG bit in the HashCode field.
80 // For this reason, each time a hashcode is calculated, it needs
81 // to be ORed with HASH_FLAG before comparing it with the save hashcode.
82 // "touchedSlots" and "emptySlot" manage the free space in the heap
84 const int INITIAL_SIZE = 10;
85 const float DEFAULT_LOAD_FACTOR = (90f / 100);
86 const int NO_SLOT = -1;
87 const int HASH_FLAG = -2147483648;
89 // The hash table contains indices into the linkSlots array
92 // All (key,value) pairs are chained into linked lists. The connection
93 // information is stored in "linkSlots" along with the key's hash code
94 // (for performance reasons).
95 // TODO: get rid of the hash code in Link (this depends on a few
96 // JIT-compiler optimizations)
97 // Every link in "linkSlots" corresponds to the (key,value) pair
98 // in "keySlots"/"valueSlots" with the same index.
101 TValue [] valueSlots;
103 // The number of slots in "linkSlots" and "keySlots"/"valueSlots" that
104 // are in use (i.e. filled with data) or have been used and marked as
108 // The index of the first slot in the "empty slots chain".
109 // "Remove()" prepends the cleared slots to the empty chain.
110 // "Add()" fills the first slot in the empty slots chain with the
111 // added item (or increases "touchedSlots" if the chain itself is empty).
114 // The number of (key,value) pairs in this dictionary.
117 // The number of (key,value) pairs the dictionary can hold without
118 // resizing the hash table and the slots arrays.
121 IEqualityComparer<TKey> hcp;
122 SerializationInfo serialization_info;
124 // The number of changes made to this dictionary. Used by enumerators
125 // to detect changes and invalidate themselves.
129 get { return count; }
132 public TValue this [TKey key] {
135 throw new ArgumentNullException ("key");
137 // get first item of linked list corresponding to given key
138 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
139 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
141 // walk linked list until right slot is found or end is reached
142 while (cur != NO_SLOT) {
143 // The ordering is important for compatibility with MS and strange
144 // Object.Equals () implementations
145 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
146 return valueSlots [cur];
147 cur = linkSlots [cur].Next;
149 throw new KeyNotFoundException ();
154 throw new ArgumentNullException ("key");
156 // get first item of linked list corresponding to given key
157 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
158 int index = (hashCode & int.MaxValue) % table.Length;
159 int cur = table [index] - 1;
161 // walk linked list until right slot (and its predecessor) is
162 // found or end is reached
164 if (cur != NO_SLOT) {
166 // The ordering is important for compatibility with MS and strange
167 // Object.Equals () implementations
168 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
171 cur = linkSlots [cur].Next;
172 } while (cur != NO_SLOT);
175 // is there no slot for the given key yet?
176 if (cur == NO_SLOT) {
177 // there is no existing slot for the given key,
178 // allocate one and prepend it to its corresponding linked
181 if (++count > threshold) {
183 index = (hashCode & int.MaxValue) % table.Length;
186 // find an empty slot
189 cur = touchedSlots++;
191 emptySlot = linkSlots [cur].Next;
193 // prepend the added item to its linked list,
194 // update the hash table
195 linkSlots [cur].Next = table [index] - 1;
196 table [index] = cur + 1;
198 // store the new item and its hash code
199 linkSlots [cur].HashCode = hashCode;
200 keySlots [cur] = key;
202 // we already have a slot for the given key,
203 // update the existing slot
205 // if the slot is not at the front of its linked list,
207 if (prev != NO_SLOT) {
208 linkSlots [prev].Next = linkSlots [cur].Next;
209 linkSlots [cur].Next = table [index] - 1;
210 table [index] = cur + 1;
214 // store the item's data itself
215 valueSlots [cur] = value;
223 Init (INITIAL_SIZE, null);
226 public Dictionary (IEqualityComparer<TKey> comparer)
228 Init (INITIAL_SIZE, comparer);
231 public Dictionary (IDictionary<TKey, TValue> dictionary)
232 : this (dictionary, null)
236 public Dictionary (int capacity)
238 Init (capacity, null);
241 public Dictionary (IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
243 if (dictionary == null)
244 throw new ArgumentNullException ("dictionary");
245 int capacity = dictionary.Count;
246 Init (capacity, comparer);
247 foreach (KeyValuePair<TKey, TValue> entry in dictionary)
248 this.Add (entry.Key, entry.Value);
251 public Dictionary (int capacity, IEqualityComparer<TKey> comparer)
253 Init (capacity, comparer);
256 protected Dictionary (SerializationInfo info, StreamingContext context)
258 serialization_info = info;
261 private void Init (int capacity, IEqualityComparer<TKey> hcp)
264 throw new ArgumentOutOfRangeException ("capacity");
265 this.hcp = (hcp != null) ? hcp : EqualityComparer<TKey>.Default;
267 capacity = INITIAL_SIZE;
269 /* Modify capacity so 'capacity' elements can be added without resizing */
270 capacity = (int)(capacity / DEFAULT_LOAD_FACTOR) + 1;
272 InitArrays (capacity);
276 private void InitArrays (int size) {
277 table = new int [size];
279 linkSlots = new Link [size];
282 keySlots = new TKey [size];
283 valueSlots = new TValue [size];
286 threshold = (int)(table.Length * DEFAULT_LOAD_FACTOR);
287 if (threshold == 0 && table.Length > 0)
291 void CopyToCheck (Array array, int index)
294 throw new ArgumentNullException ("array");
296 throw new ArgumentOutOfRangeException ("index");
297 // we want no exception for index==array.Length && Count == 0
298 if (index > array.Length)
299 throw new ArgumentException ("index larger than largest valid index of array");
300 if (array.Length - index < Count)
301 throw new ArgumentException ("Destination array cannot hold the requested elements!");
304 delegate TRet Transform<TRet> (TKey key, TValue value);
306 void Do_CopyTo<TRet, TElem> (TElem [] array, int index, Transform<TRet> transform)
309 for (int i = 0; i < touchedSlots; i++) {
310 if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
311 array [index++] = transform (keySlots [i], valueSlots [i]);
315 static KeyValuePair<TKey, TValue> make_pair (TKey key, TValue value)
317 return new KeyValuePair<TKey, TValue> (key, value);
320 static TKey pick_key (TKey key, TValue value)
325 static TValue pick_value (TKey key, TValue value)
330 void CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
332 CopyToCheck (array, index);
333 Do_CopyTo<KeyValuePair<TKey, TValue>, KeyValuePair<TKey, TValue>> (array, index, make_pair);
336 void Do_ICollectionCopyTo<TRet> (Array array, int index, Transform<TRet> transform)
338 Type src = typeof (TRet);
339 Type tgt = array.GetType ().GetElementType ();
342 if ((src.IsPrimitive || tgt.IsPrimitive) && !tgt.IsAssignableFrom (src))
343 throw new Exception (); // we don't care. it'll get transformed to an ArgumentException below
346 // BOOTSTRAP: gmcs 2.4.x seems to have trouble compiling the alternative
347 throw new Exception ();
349 Do_CopyTo ((object []) array, index, transform);
352 } catch (Exception e) {
353 throw new ArgumentException ("Cannot copy source collection elements to destination array", "array", e);
357 private void Resize ()
359 // From the SDK docs:
360 // Hashtable is automatically increased
361 // to the smallest prime number that is larger
362 // than twice the current number of Hashtable buckets
363 int newSize = Hashtable.ToPrime ((table.Length << 1) | 1);
365 // allocate new hash table and link slots array
366 int [] newTable = new int [newSize];
367 Link [] newLinkSlots = new Link [newSize];
369 for (int i = 0; i < table.Length; i++) {
370 int cur = table [i] - 1;
371 while (cur != NO_SLOT) {
372 int hashCode = newLinkSlots [cur].HashCode = hcp.GetHashCode(keySlots [cur]) | HASH_FLAG;
373 int index = (hashCode & int.MaxValue) % newSize;
374 newLinkSlots [cur].Next = newTable [index] - 1;
375 newTable [index] = cur + 1;
376 cur = linkSlots [cur].Next;
380 linkSlots = newLinkSlots;
382 // allocate new data slots, copy data
383 TKey [] newKeySlots = new TKey [newSize];
384 TValue [] newValueSlots = new TValue [newSize];
385 Array.Copy (keySlots, 0, newKeySlots, 0, touchedSlots);
386 Array.Copy (valueSlots, 0, newValueSlots, 0, touchedSlots);
387 keySlots = newKeySlots;
388 valueSlots = newValueSlots;
390 threshold = (int)(newSize * DEFAULT_LOAD_FACTOR);
393 public void Add (TKey key, TValue value)
396 throw new ArgumentNullException ("key");
398 // get first item of linked list corresponding to given key
399 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
400 int index = (hashCode & int.MaxValue) % table.Length;
401 int cur = table [index] - 1;
403 // walk linked list until end is reached (throw an exception if a
404 // existing slot is found having an equivalent key)
405 while (cur != NO_SLOT) {
406 // The ordering is important for compatibility with MS and strange
407 // Object.Equals () implementations
408 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
409 throw new ArgumentException ("An element with the same key already exists in the dictionary.");
410 cur = linkSlots [cur].Next;
413 if (++count > threshold) {
415 index = (hashCode & int.MaxValue) % table.Length;
418 // find an empty slot
421 cur = touchedSlots++;
423 emptySlot = linkSlots [cur].Next;
425 // store the hash code of the added item,
426 // prepend the added item to its linked list,
427 // update the hash table
428 linkSlots [cur].HashCode = hashCode;
429 linkSlots [cur].Next = table [index] - 1;
430 table [index] = cur + 1;
433 keySlots [cur] = key;
434 valueSlots [cur] = value;
439 public IEqualityComparer<TKey> Comparer {
446 // clear the hash table
447 Array.Clear (table, 0, table.Length);
449 Array.Clear (keySlots, 0, keySlots.Length);
450 Array.Clear (valueSlots, 0, valueSlots.Length);
451 Array.Clear (linkSlots, 0, linkSlots.Length);
453 // empty the "empty slots chain"
460 public bool ContainsKey (TKey key)
463 throw new ArgumentNullException ("key");
465 // get first item of linked list corresponding to given key
466 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
467 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
469 // walk linked list until right slot is found or end is reached
470 while (cur != NO_SLOT) {
471 // The ordering is important for compatibility with MS and strange
472 // Object.Equals () implementations
473 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
475 cur = linkSlots [cur].Next;
481 public bool ContainsValue (TValue value)
483 IEqualityComparer<TValue> cmp = EqualityComparer<TValue>.Default;
485 for (int i = 0; i < table.Length; i++) {
486 int cur = table [i] - 1;
487 while (cur != NO_SLOT) {
488 if (cmp.Equals (valueSlots [cur], value))
490 cur = linkSlots [cur].Next;
496 [SecurityPermission (SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
497 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
500 throw new ArgumentNullException ("info");
502 info.AddValue ("Version", generation);
503 info.AddValue ("Comparer", hcp);
504 KeyValuePair<TKey, TValue> [] data = null;
505 data = new KeyValuePair<TKey,TValue> [count];
508 info.AddValue ("HashSize", table.Length);
509 info.AddValue ("KeyValuePairs", data);
512 public virtual void OnDeserialization (object sender)
514 if (serialization_info == null)
517 generation = serialization_info.GetInt32 ("Version");
518 hcp = (IEqualityComparer<TKey>) serialization_info.GetValue ("Comparer", typeof (IEqualityComparer<TKey>));
520 int hashSize = serialization_info.GetInt32 ("HashSize");
521 KeyValuePair<TKey, TValue> [] data =
522 (KeyValuePair<TKey, TValue> [])
523 serialization_info.GetValue ("KeyValuePairs", typeof (KeyValuePair<TKey, TValue> []));
525 if (hashSize < INITIAL_SIZE)
526 hashSize = INITIAL_SIZE;
527 InitArrays (hashSize);
531 for (int i = 0; i < data.Length; ++i)
532 Add (data [i].Key, data [i].Value);
535 serialization_info = null;
538 public bool Remove (TKey key)
541 throw new ArgumentNullException ("key");
543 // get first item of linked list corresponding to given key
544 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
545 int index = (hashCode & int.MaxValue) % table.Length;
546 int cur = table [index] - 1;
548 // if there is no linked list, return false
552 // walk linked list until right slot (and its predecessor) is
553 // found or end is reached
556 // The ordering is important for compatibility with MS and strange
557 // Object.Equals () implementations
558 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
561 cur = linkSlots [cur].Next;
562 } while (cur != NO_SLOT);
564 // if we reached the end of the chain, return false
569 // remove slot from linked list
570 // is slot at beginning of linked list?
572 table [index] = linkSlots [cur].Next + 1;
574 linkSlots [prev].Next = linkSlots [cur].Next;
576 // mark slot as empty and prepend it to "empty slots chain"
577 linkSlots [cur].Next = emptySlot;
580 linkSlots [cur].HashCode = 0;
581 // clear empty key and value slots
582 keySlots [cur] = default (TKey);
583 valueSlots [cur] = default (TValue);
589 public bool TryGetValue (TKey key, out TValue value)
592 throw new ArgumentNullException ("key");
594 // get first item of linked list corresponding to given key
595 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
596 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
598 // walk linked list until right slot is found or end is reached
599 while (cur != NO_SLOT) {
600 // The ordering is important for compatibility with MS and strange
601 // Object.Equals () implementations
602 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key)) {
603 value = valueSlots [cur];
606 cur = linkSlots [cur].Next;
609 // we did not find the slot
610 value = default (TValue);
614 ICollection<TKey> IDictionary<TKey, TValue>.Keys {
618 ICollection<TValue> IDictionary<TKey, TValue>.Values {
619 get { return Values; }
622 public KeyCollection Keys {
623 get { return new KeyCollection (this); }
626 public ValueCollection Values {
627 get { return new ValueCollection (this); }
630 ICollection IDictionary.Keys {
634 ICollection IDictionary.Values {
635 get { return Values; }
638 bool IDictionary.IsFixedSize {
639 get { return false; }
642 bool IDictionary.IsReadOnly {
643 get { return false; }
646 TKey ToTKey (object key)
649 throw new ArgumentNullException ("key");
651 throw new ArgumentException ("not of type: " + typeof (TKey).ToString (), "key");
655 TValue ToTValue (object value)
657 if (value == null && !typeof (TValue).IsValueType)
658 return default (TValue);
659 if (!(value is TValue))
660 throw new ArgumentException ("not of type: " + typeof (TValue).ToString (), "value");
661 return (TValue) value;
664 object IDictionary.this [object key] {
666 if (key is TKey && ContainsKey((TKey) key))
667 return this [ToTKey (key)];
670 set { this [ToTKey (key)] = ToTValue (value); }
673 void IDictionary.Add (object key, object value)
675 this.Add (ToTKey (key), ToTValue (value));
678 bool IDictionary.Contains (object key)
681 throw new ArgumentNullException ("key");
683 return ContainsKey ((TKey) key);
687 void IDictionary.Remove (object key)
690 throw new ArgumentNullException ("key");
695 bool ICollection.IsSynchronized {
696 get { return false; }
699 object ICollection.SyncRoot {
703 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
704 get { return false; }
707 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair)
709 Add (keyValuePair.Key, keyValuePair.Value);
712 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair)
714 return ContainsKeyValuePair (keyValuePair);
717 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
719 this.CopyTo (array, index);
722 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair)
724 if (!ContainsKeyValuePair (keyValuePair))
727 return Remove (keyValuePair.Key);
730 bool ContainsKeyValuePair (KeyValuePair<TKey, TValue> pair)
733 if (!TryGetValue (pair.Key, out value))
736 return EqualityComparer<TValue>.Default.Equals (pair.Value, value);
739 void ICollection.CopyTo (Array array, int index)
741 KeyValuePair<TKey, TValue> [] pairs = array as KeyValuePair<TKey, TValue> [];
743 this.CopyTo (pairs, index);
747 CopyToCheck (array, index);
748 DictionaryEntry [] entries = array as DictionaryEntry [];
749 if (entries != null) {
750 Do_CopyTo (entries, index, delegate (TKey key, TValue value) { return new DictionaryEntry (key, value); });
754 Do_ICollectionCopyTo<KeyValuePair<TKey, TValue>> (array, index, make_pair);
757 IEnumerator IEnumerable.GetEnumerator ()
759 return new Enumerator (this);
762 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
764 return new Enumerator (this);
767 IDictionaryEnumerator IDictionary.GetEnumerator ()
769 return new ShimEnumerator (this);
772 public Enumerator GetEnumerator ()
774 return new Enumerator (this);
778 private class ShimEnumerator : IDictionaryEnumerator, IEnumerator
780 Enumerator host_enumerator;
781 public ShimEnumerator (Dictionary<TKey, TValue> host)
783 host_enumerator = host.GetEnumerator ();
786 public void Dispose ()
788 host_enumerator.Dispose ();
791 public bool MoveNext ()
793 return host_enumerator.MoveNext ();
796 public DictionaryEntry Entry {
797 get { return ((IDictionaryEnumerator) host_enumerator).Entry; }
801 get { return host_enumerator.Current.Key; }
804 public object Value {
805 get { return host_enumerator.Current.Value; }
808 // This is the raison d' etre of this $%!@$%@^@ class.
809 // We want: IDictionary.GetEnumerator ().Current is DictionaryEntry
810 public object Current {
811 get { return Entry; }
816 host_enumerator.Reset ();
821 public struct Enumerator : IEnumerator<KeyValuePair<TKey,TValue>>,
822 IDisposable, IDictionaryEnumerator, IEnumerator
824 Dictionary<TKey, TValue> dictionary;
828 internal KeyValuePair<TKey, TValue> current;
830 internal Enumerator (Dictionary<TKey, TValue> dictionary)
833 this.dictionary = dictionary;
834 stamp = dictionary.generation;
837 public bool MoveNext ()
844 while (next < dictionary.touchedSlots) {
846 if ((dictionary.linkSlots [cur].HashCode & HASH_FLAG) != 0) {
847 current = new KeyValuePair <TKey, TValue> (
848 dictionary.keySlots [cur],
849 dictionary.valueSlots [cur]
859 // No error checking happens. Usually, Current is immediately preceded by a MoveNext(), so it's wasteful to check again
860 public KeyValuePair<TKey, TValue> Current {
861 get { return current; }
864 internal TKey CurrentKey {
871 internal TValue CurrentValue {
874 return current.Value;
878 object IEnumerator.Current {
885 void IEnumerator.Reset ()
890 internal void Reset ()
896 DictionaryEntry IDictionaryEnumerator.Entry {
899 return new DictionaryEntry (current.Key, current.Value);
903 object IDictionaryEnumerator.Key {
904 get { return CurrentKey; }
907 object IDictionaryEnumerator.Value {
908 get { return CurrentValue; }
913 if (dictionary == null)
914 throw new ObjectDisposedException (null);
915 if (dictionary.generation != stamp)
916 throw new InvalidOperationException ("out of sync");
919 void VerifyCurrent ()
923 throw new InvalidOperationException ("Current is not valid");
926 public void Dispose ()
932 // This collection is a read only collection
934 [DebuggerDisplay ("Count={Count}")]
935 [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
936 public sealed class KeyCollection : ICollection<TKey>, IEnumerable<TKey>, ICollection, IEnumerable {
937 Dictionary<TKey, TValue> dictionary;
939 public KeyCollection (Dictionary<TKey, TValue> dictionary)
941 if (dictionary == null)
942 throw new ArgumentNullException ("dictionary");
943 this.dictionary = dictionary;
947 public void CopyTo (TKey [] array, int index)
949 dictionary.CopyToCheck (array, index);
950 dictionary.Do_CopyTo<TKey, TKey> (array, index, pick_key);
953 public Enumerator GetEnumerator ()
955 return new Enumerator (dictionary);
958 void ICollection<TKey>.Add (TKey item)
960 throw new NotSupportedException ("this is a read-only collection");
963 void ICollection<TKey>.Clear ()
965 throw new NotSupportedException ("this is a read-only collection");
968 bool ICollection<TKey>.Contains (TKey item)
970 return dictionary.ContainsKey (item);
973 bool ICollection<TKey>.Remove (TKey item)
975 throw new NotSupportedException ("this is a read-only collection");
978 IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator ()
980 return this.GetEnumerator ();
983 void ICollection.CopyTo (Array array, int index)
985 var target = array as TKey [];
986 if (target != null) {
987 CopyTo (target, index);
991 dictionary.CopyToCheck (array, index);
992 dictionary.Do_ICollectionCopyTo<TKey> (array, index, pick_key);
995 IEnumerator IEnumerable.GetEnumerator ()
997 return this.GetEnumerator ();
1001 get { return dictionary.Count; }
1004 bool ICollection<TKey>.IsReadOnly {
1005 get { return true; }
1008 bool ICollection.IsSynchronized {
1009 get { return false; }
1012 object ICollection.SyncRoot {
1013 get { return ((ICollection) dictionary).SyncRoot; }
1017 public struct Enumerator : IEnumerator<TKey>, IDisposable, IEnumerator {
1018 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1020 internal Enumerator (Dictionary<TKey, TValue> host)
1022 host_enumerator = host.GetEnumerator ();
1025 public void Dispose ()
1027 host_enumerator.Dispose ();
1030 public bool MoveNext ()
1032 return host_enumerator.MoveNext ();
1035 public TKey Current {
1036 get { return host_enumerator.current.Key; }
1039 object IEnumerator.Current {
1040 get { return host_enumerator.CurrentKey; }
1043 void IEnumerator.Reset ()
1045 host_enumerator.Reset ();
1050 // This collection is a read only collection
1052 [DebuggerDisplay ("Count={Count}")]
1053 [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
1054 public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable {
1055 Dictionary<TKey, TValue> dictionary;
1057 public ValueCollection (Dictionary<TKey, TValue> dictionary)
1059 if (dictionary == null)
1060 throw new ArgumentNullException ("dictionary");
1061 this.dictionary = dictionary;
1064 public void CopyTo (TValue [] array, int index)
1066 dictionary.CopyToCheck (array, index);
1067 dictionary.Do_CopyTo<TValue, TValue> (array, index, pick_value);
1070 public Enumerator GetEnumerator ()
1072 return new Enumerator (dictionary);
1075 void ICollection<TValue>.Add (TValue item)
1077 throw new NotSupportedException ("this is a read-only collection");
1080 void ICollection<TValue>.Clear ()
1082 throw new NotSupportedException ("this is a read-only collection");
1085 bool ICollection<TValue>.Contains (TValue item)
1087 return dictionary.ContainsValue (item);
1090 bool ICollection<TValue>.Remove (TValue item)
1092 throw new NotSupportedException ("this is a read-only collection");
1095 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator ()
1097 return this.GetEnumerator ();
1100 void ICollection.CopyTo (Array array, int index)
1102 var target = array as TValue [];
1103 if (target != null) {
1104 CopyTo (target, index);
1108 dictionary.CopyToCheck (array, index);
1109 dictionary.Do_ICollectionCopyTo<TValue> (array, index, pick_value);
1112 IEnumerator IEnumerable.GetEnumerator ()
1114 return this.GetEnumerator ();
1118 get { return dictionary.Count; }
1121 bool ICollection<TValue>.IsReadOnly {
1122 get { return true; }
1125 bool ICollection.IsSynchronized {
1126 get { return false; }
1129 object ICollection.SyncRoot {
1130 get { return ((ICollection) dictionary).SyncRoot; }
1134 public struct Enumerator : IEnumerator<TValue>, IDisposable, IEnumerator {
1135 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1137 internal Enumerator (Dictionary<TKey,TValue> host)
1139 host_enumerator = host.GetEnumerator ();
1142 public void Dispose ()
1144 host_enumerator.Dispose ();
1147 public bool MoveNext ()
1149 return host_enumerator.MoveNext ();
1152 public TValue Current {
1153 get { return host_enumerator.current.Value; }
1156 object IEnumerator.Current {
1157 get { return host_enumerator.CurrentValue; }
1160 void IEnumerator.Reset ()
1162 host_enumerator.Reset ();