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 // MS.NET expects either *no* KeyValuePairs field (when count = 0)
505 // or a non-null KeyValuePairs field. We don't omit the field to
506 // remain compatible with older monos, but we also doesn't serialize
507 // it as null to make MS.NET happy.
508 KeyValuePair
<TKey
, TValue
> [] data
= new KeyValuePair
<TKey
,TValue
> [count
];
511 info
.AddValue ("HashSize", table
.Length
);
512 info
.AddValue ("KeyValuePairs", data
);
515 public virtual void OnDeserialization (object sender
)
517 if (serialization_info
== null)
521 KeyValuePair
<TKey
, TValue
> [] data
= null;
523 // We must use the enumerator because MS.NET doesn't
524 // serialize "KeyValuePairs" for count = 0.
525 SerializationInfoEnumerator e
= serialization_info
.GetEnumerator ();
526 while (e
.MoveNext ()) {
529 generation
= (int) e
.Value
;
533 hcp
= (IEqualityComparer
<TKey
>) e
.Value
;
537 hashSize
= (int) e
.Value
;
540 case "KeyValuePairs":
541 data
= (KeyValuePair
<TKey
, TValue
> []) e
.Value
;
547 hcp
= EqualityComparer
<TKey
>.Default
;
548 if (hashSize
< INITIAL_SIZE
)
549 hashSize
= INITIAL_SIZE
;
550 InitArrays (hashSize
);
554 for (int i
= 0; i
< data
.Length
; ++i
)
555 Add (data
[i
].Key
, data
[i
].Value
);
558 serialization_info
= null;
561 public bool Remove (TKey key
)
564 throw new ArgumentNullException ("key");
566 // get first item of linked list corresponding to given key
567 int hashCode
= hcp
.GetHashCode (key
) | HASH_FLAG
;
568 int index
= (hashCode
& int.MaxValue
) % table
.Length
;
569 int cur
= table
[index
] - 1;
571 // if there is no linked list, return false
575 // walk linked list until right slot (and its predecessor) is
576 // found or end is reached
579 // The ordering is important for compatibility with MS and strange
580 // Object.Equals () implementations
581 if (linkSlots
[cur
].HashCode
== hashCode
&& hcp
.Equals (keySlots
[cur
], key
))
584 cur
= linkSlots
[cur
].Next
;
585 } while (cur
!= NO_SLOT
);
587 // if we reached the end of the chain, return false
592 // remove slot from linked list
593 // is slot at beginning of linked list?
595 table
[index
] = linkSlots
[cur
].Next
+ 1;
597 linkSlots
[prev
].Next
= linkSlots
[cur
].Next
;
599 // mark slot as empty and prepend it to "empty slots chain"
600 linkSlots
[cur
].Next
= emptySlot
;
603 linkSlots
[cur
].HashCode
= 0;
604 // clear empty key and value slots
605 keySlots
[cur
] = default (TKey
);
606 valueSlots
[cur
] = default (TValue
);
612 public bool TryGetValue (TKey key
, out TValue
value)
615 throw new ArgumentNullException ("key");
617 // get first item of linked list corresponding to given key
618 int hashCode
= hcp
.GetHashCode (key
) | HASH_FLAG
;
619 int cur
= table
[(hashCode
& int.MaxValue
) % table
.Length
] - 1;
621 // walk linked list until right slot is found or end is reached
622 while (cur
!= NO_SLOT
) {
623 // The ordering is important for compatibility with MS and strange
624 // Object.Equals () implementations
625 if (linkSlots
[cur
].HashCode
== hashCode
&& hcp
.Equals (keySlots
[cur
], key
)) {
626 value = valueSlots
[cur
];
629 cur
= linkSlots
[cur
].Next
;
632 // we did not find the slot
633 value = default (TValue
);
637 ICollection
<TKey
> IDictionary
<TKey
, TValue
>.Keys
{
641 ICollection
<TValue
> IDictionary
<TKey
, TValue
>.Values
{
642 get { return Values; }
645 public KeyCollection Keys
{
646 get { return new KeyCollection (this); }
649 public ValueCollection Values
{
650 get { return new ValueCollection (this); }
653 ICollection IDictionary
.Keys
{
657 ICollection IDictionary
.Values
{
658 get { return Values; }
661 bool IDictionary
.IsFixedSize
{
662 get { return false; }
665 bool IDictionary
.IsReadOnly
{
666 get { return false; }
669 TKey
ToTKey (object key
)
672 throw new ArgumentNullException ("key");
674 throw new ArgumentException ("not of type: " + typeof (TKey
).ToString (), "key");
678 TValue
ToTValue (object value)
680 if (value == null && !typeof (TValue
).IsValueType
)
681 return default (TValue
);
682 if (!(value is TValue
))
683 throw new ArgumentException ("not of type: " + typeof (TValue
).ToString (), "value");
684 return (TValue
) value;
687 object IDictionary
.this [object key
] {
689 if (key
is TKey
&& ContainsKey((TKey
) key
))
690 return this [ToTKey (key
)];
693 set { this [ToTKey (key)] = ToTValue (value); }
696 void IDictionary
.Add (object key
, object value)
698 this.Add (ToTKey (key
), ToTValue (value));
701 bool IDictionary
.Contains (object key
)
704 throw new ArgumentNullException ("key");
706 return ContainsKey ((TKey
) key
);
710 void IDictionary
.Remove (object key
)
713 throw new ArgumentNullException ("key");
718 bool ICollection
.IsSynchronized
{
719 get { return false; }
722 object ICollection
.SyncRoot
{
726 bool ICollection
<KeyValuePair
<TKey
, TValue
>>.IsReadOnly
{
727 get { return false; }
730 void ICollection
<KeyValuePair
<TKey
, TValue
>>.Add (KeyValuePair
<TKey
, TValue
> keyValuePair
)
732 Add (keyValuePair
.Key
, keyValuePair
.Value
);
735 bool ICollection
<KeyValuePair
<TKey
, TValue
>>.Contains (KeyValuePair
<TKey
, TValue
> keyValuePair
)
737 return ContainsKeyValuePair (keyValuePair
);
740 void ICollection
<KeyValuePair
<TKey
, TValue
>>.CopyTo (KeyValuePair
<TKey
, TValue
> [] array
, int index
)
742 this.CopyTo (array
, index
);
745 bool ICollection
<KeyValuePair
<TKey
, TValue
>>.Remove (KeyValuePair
<TKey
, TValue
> keyValuePair
)
747 if (!ContainsKeyValuePair (keyValuePair
))
750 return Remove (keyValuePair
.Key
);
753 bool ContainsKeyValuePair (KeyValuePair
<TKey
, TValue
> pair
)
756 if (!TryGetValue (pair
.Key
, out value))
759 return EqualityComparer
<TValue
>.Default
.Equals (pair
.Value
, value);
762 void ICollection
.CopyTo (Array array
, int index
)
764 KeyValuePair
<TKey
, TValue
> [] pairs
= array
as KeyValuePair
<TKey
, TValue
> [];
766 this.CopyTo (pairs
, index
);
770 CopyToCheck (array
, index
);
771 DictionaryEntry
[] entries
= array
as DictionaryEntry
[];
772 if (entries
!= null) {
773 Do_CopyTo (entries
, index
, delegate (TKey key
, TValue
value) { return new DictionaryEntry (key, value); }
);
777 Do_ICollectionCopyTo
<KeyValuePair
<TKey
, TValue
>> (array
, index
, make_pair
);
780 IEnumerator IEnumerable
.GetEnumerator ()
782 return new Enumerator (this);
785 IEnumerator
<KeyValuePair
<TKey
, TValue
>> IEnumerable
<KeyValuePair
<TKey
, TValue
>>.GetEnumerator ()
787 return new Enumerator (this);
790 IDictionaryEnumerator IDictionary
.GetEnumerator ()
792 return new ShimEnumerator (this);
795 public Enumerator
GetEnumerator ()
797 return new Enumerator (this);
801 private class ShimEnumerator
: IDictionaryEnumerator
, IEnumerator
803 Enumerator host_enumerator
;
804 public ShimEnumerator (Dictionary
<TKey
, TValue
> host
)
806 host_enumerator
= host
.GetEnumerator ();
809 public void Dispose ()
811 host_enumerator
.Dispose ();
814 public bool MoveNext ()
816 return host_enumerator
.MoveNext ();
819 public DictionaryEntry Entry
{
820 get { return ((IDictionaryEnumerator) host_enumerator).Entry; }
824 get { return host_enumerator.Current.Key; }
827 public object Value
{
828 get { return host_enumerator.Current.Value; }
831 // This is the raison d' etre of this $%!@$%@^@ class.
832 // We want: IDictionary.GetEnumerator ().Current is DictionaryEntry
833 public object Current
{
834 get { return Entry; }
839 host_enumerator
.Reset ();
844 public struct Enumerator
: IEnumerator
<KeyValuePair
<TKey
,TValue
>>,
845 IDisposable
, IDictionaryEnumerator
, IEnumerator
847 Dictionary
<TKey
, TValue
> dictionary
;
851 internal KeyValuePair
<TKey
, TValue
> current
;
853 internal Enumerator (Dictionary
<TKey
, TValue
> dictionary
)
856 this.dictionary
= dictionary
;
857 stamp
= dictionary
.generation
;
860 public bool MoveNext ()
867 while (next
< dictionary
.touchedSlots
) {
869 if ((dictionary
.linkSlots
[cur
].HashCode
& HASH_FLAG
) != 0) {
870 current
= new KeyValuePair
<TKey
, TValue
> (
871 dictionary
.keySlots
[cur
],
872 dictionary
.valueSlots
[cur
]
882 // No error checking happens. Usually, Current is immediately preceded by a MoveNext(), so it's wasteful to check again
883 public KeyValuePair
<TKey
, TValue
> Current
{
884 get { return current; }
887 internal TKey CurrentKey
{
894 internal TValue CurrentValue
{
897 return current
.Value
;
901 object IEnumerator
.Current
{
908 void IEnumerator
.Reset ()
913 internal void Reset ()
919 DictionaryEntry IDictionaryEnumerator
.Entry
{
922 return new DictionaryEntry (current
.Key
, current
.Value
);
926 object IDictionaryEnumerator
.Key
{
927 get { return CurrentKey; }
930 object IDictionaryEnumerator
.Value
{
931 get { return CurrentValue; }
936 if (dictionary
== null)
937 throw new ObjectDisposedException (null);
938 if (dictionary
.generation
!= stamp
)
939 throw new InvalidOperationException ("out of sync");
942 void VerifyCurrent ()
946 throw new InvalidOperationException ("Current is not valid");
949 public void Dispose ()
955 // This collection is a read only collection
957 [DebuggerDisplay ("Count={Count}")]
958 [DebuggerTypeProxy (typeof (CollectionDebuggerView
<,>))]
959 public sealed class KeyCollection
: ICollection
<TKey
>, IEnumerable
<TKey
>, ICollection
, IEnumerable
{
960 Dictionary
<TKey
, TValue
> dictionary
;
962 public KeyCollection (Dictionary
<TKey
, TValue
> dictionary
)
964 if (dictionary
== null)
965 throw new ArgumentNullException ("dictionary");
966 this.dictionary
= dictionary
;
970 public void CopyTo (TKey
[] array
, int index
)
972 dictionary
.CopyToCheck (array
, index
);
973 dictionary
.Do_CopyTo
<TKey
, TKey
> (array
, index
, pick_key
);
976 public Enumerator
GetEnumerator ()
978 return new Enumerator (dictionary
);
981 void ICollection
<TKey
>.Add (TKey item
)
983 throw new NotSupportedException ("this is a read-only collection");
986 void ICollection
<TKey
>.Clear ()
988 throw new NotSupportedException ("this is a read-only collection");
991 bool ICollection
<TKey
>.Contains (TKey item
)
993 return dictionary
.ContainsKey (item
);
996 bool ICollection
<TKey
>.Remove (TKey item
)
998 throw new NotSupportedException ("this is a read-only collection");
1001 IEnumerator
<TKey
> IEnumerable
<TKey
>.GetEnumerator ()
1003 return this.GetEnumerator ();
1006 void ICollection
.CopyTo (Array array
, int index
)
1008 var target
= array
as TKey
[];
1009 if (target
!= null) {
1010 CopyTo (target
, index
);
1014 dictionary
.CopyToCheck (array
, index
);
1015 dictionary
.Do_ICollectionCopyTo
<TKey
> (array
, index
, pick_key
);
1018 IEnumerator IEnumerable
.GetEnumerator ()
1020 return this.GetEnumerator ();
1024 get { return dictionary.Count; }
1027 bool ICollection
<TKey
>.IsReadOnly
{
1028 get { return true; }
1031 bool ICollection
.IsSynchronized
{
1032 get { return false; }
1035 object ICollection
.SyncRoot
{
1036 get { return ((ICollection) dictionary).SyncRoot; }
1040 public struct Enumerator
: IEnumerator
<TKey
>, IDisposable
, IEnumerator
{
1041 Dictionary
<TKey
, TValue
>.Enumerator host_enumerator
;
1043 internal Enumerator (Dictionary
<TKey
, TValue
> host
)
1045 host_enumerator
= host
.GetEnumerator ();
1048 public void Dispose ()
1050 host_enumerator
.Dispose ();
1053 public bool MoveNext ()
1055 return host_enumerator
.MoveNext ();
1058 public TKey Current
{
1059 get { return host_enumerator.current.Key; }
1062 object IEnumerator
.Current
{
1063 get { return host_enumerator.CurrentKey; }
1066 void IEnumerator
.Reset ()
1068 host_enumerator
.Reset ();
1073 // This collection is a read only collection
1075 [DebuggerDisplay ("Count={Count}")]
1076 [DebuggerTypeProxy (typeof (CollectionDebuggerView
<,>))]
1077 public sealed class ValueCollection
: ICollection
<TValue
>, IEnumerable
<TValue
>, ICollection
, IEnumerable
{
1078 Dictionary
<TKey
, TValue
> dictionary
;
1080 public ValueCollection (Dictionary
<TKey
, TValue
> dictionary
)
1082 if (dictionary
== null)
1083 throw new ArgumentNullException ("dictionary");
1084 this.dictionary
= dictionary
;
1087 public void CopyTo (TValue
[] array
, int index
)
1089 dictionary
.CopyToCheck (array
, index
);
1090 dictionary
.Do_CopyTo
<TValue
, TValue
> (array
, index
, pick_value
);
1093 public Enumerator
GetEnumerator ()
1095 return new Enumerator (dictionary
);
1098 void ICollection
<TValue
>.Add (TValue item
)
1100 throw new NotSupportedException ("this is a read-only collection");
1103 void ICollection
<TValue
>.Clear ()
1105 throw new NotSupportedException ("this is a read-only collection");
1108 bool ICollection
<TValue
>.Contains (TValue item
)
1110 return dictionary
.ContainsValue (item
);
1113 bool ICollection
<TValue
>.Remove (TValue item
)
1115 throw new NotSupportedException ("this is a read-only collection");
1118 IEnumerator
<TValue
> IEnumerable
<TValue
>.GetEnumerator ()
1120 return this.GetEnumerator ();
1123 void ICollection
.CopyTo (Array array
, int index
)
1125 var target
= array
as TValue
[];
1126 if (target
!= null) {
1127 CopyTo (target
, index
);
1131 dictionary
.CopyToCheck (array
, index
);
1132 dictionary
.Do_ICollectionCopyTo
<TValue
> (array
, index
, pick_value
);
1135 IEnumerator IEnumerable
.GetEnumerator ()
1137 return this.GetEnumerator ();
1141 get { return dictionary.Count; }
1144 bool ICollection
<TValue
>.IsReadOnly
{
1145 get { return true; }
1148 bool ICollection
.IsSynchronized
{
1149 get { return false; }
1152 object ICollection
.SyncRoot
{
1153 get { return ((ICollection) dictionary).SyncRoot; }
1157 public struct Enumerator
: IEnumerator
<TValue
>, IDisposable
, IEnumerator
{
1158 Dictionary
<TKey
, TValue
>.Enumerator host_enumerator
;
1160 internal Enumerator (Dictionary
<TKey
,TValue
> host
)
1162 host_enumerator
= host
.GetEnumerator ();
1165 public void Dispose ()
1167 host_enumerator
.Dispose ();
1170 public bool MoveNext ()
1172 return host_enumerator
.MoveNext ();
1175 public TValue Current
{
1176 get { return host_enumerator.current.Value; }
1179 object IEnumerator
.Current
{
1180 get { return host_enumerator.CurrentValue; }
1183 void IEnumerator
.Reset ()
1185 host_enumerator
.Reset ();