1 //------------------------------------------------------------------------------
2 // <copyright file="NameObjectCollectionBase.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 /*
8  * Ordered String/Object collection of name/value pairs with support for null key
9  *
10  * This class is intended to be used as a base class
11  *
12  * Copyright (c) 2000 Microsoft Corporation
13  */
14 
15 namespace System.Collections.Specialized {
16 
17     using Microsoft.Win32;
18     using System.Collections;
19     using System.Diagnostics.CodeAnalysis;
20     using System.Globalization;
21     using System.Runtime.Serialization;
22     using System.Security.Permissions;
23 
24 
25     /// <devdoc>
26     /// <para>Provides the <see langword='abstract '/>base class for a sorted collection of associated <see cref='System.String' qualify='true'/> keys
27     ///    and <see cref='System.Object' qualify='true'/> values that can be accessed either with the hash code of
28     ///    the key or with the index.</para>
29     /// </devdoc>
30     [Serializable()]
31     public abstract class NameObjectCollectionBase : ICollection, ISerializable, IDeserializationCallback {
32         // const names used for serialization
33         private const String ReadOnlyName = "ReadOnly";
34         private const String CountName = "Count";
35         private const String ComparerName = "Comparer";
36         private const String HashCodeProviderName = "HashProvider";
37         private const String KeysName = "Keys";
38         private const String ValuesName = "Values";
39         private const String KeyComparerName = "KeyComparer";
40         private const String VersionName = "Version";
41 
42         private bool _readOnly = false;
43         private ArrayList _entriesArray;
44         private IEqualityComparer _keyComparer;
45         private volatile Hashtable _entriesTable;
46         private volatile NameObjectEntry _nullKeyEntry;
47         private KeysCollection _keys;
48         private SerializationInfo _serializationInfo;
49         private int _version;
50         [NonSerialized]
51         private Object _syncRoot;
52 
53         private static StringComparer defaultComparer = StringComparer.InvariantCultureIgnoreCase;
54 
55         /// <devdoc>
56         /// <para> Creates an empty <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance with the default initial capacity and using the default case-insensitive hash
57         ///    code provider and the default case-insensitive comparer.</para>
58         /// </devdoc>
NameObjectCollectionBase()59         protected NameObjectCollectionBase() :this(defaultComparer) {
60         }
61 
NameObjectCollectionBase(IEqualityComparer equalityComparer)62         protected NameObjectCollectionBase(IEqualityComparer equalityComparer)
63         {
64            _keyComparer = (equalityComparer == null) ? defaultComparer : equalityComparer;
65            Reset();
66         }
67 
NameObjectCollectionBase(Int32 capacity, IEqualityComparer equalityComparer)68         protected NameObjectCollectionBase(Int32 capacity, IEqualityComparer equalityComparer) : this (equalityComparer)
69         {
70             Reset(capacity);
71         }
72 
73 
74         /// <devdoc>
75         /// <para>Creates an empty <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance with
76         ///    the default initial capacity and using the specified case-insensitive hash code provider and the
77         ///    specified case-insensitive comparer.</para>
78         /// </devdoc>
79 
80 #pragma warning disable 618
81         [Obsolete("Please use NameObjectCollectionBase(IEqualityComparer) instead.")]
NameObjectCollectionBase(IHashCodeProvider hashProvider, IComparer comparer)82         protected NameObjectCollectionBase(IHashCodeProvider hashProvider, IComparer comparer) {
83             _keyComparer = new CompatibleComparer( comparer, hashProvider);
84             Reset();
85         }
86 #pragma warning restore 618
87 
88         /// <devdoc>
89         /// <para>Creates an empty <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance with the specified
90         ///    initial capacity and using the specified case-insensitive hash code provider
91         ///    and the specified case-insensitive comparer.</para>
92         /// </devdoc>
93 #pragma warning disable 618
94         [Obsolete("Please use NameObjectCollectionBase(Int32, IEqualityComparer) instead.")]
NameObjectCollectionBase(int capacity, IHashCodeProvider hashProvider, IComparer comparer)95         protected NameObjectCollectionBase(int capacity, IHashCodeProvider hashProvider, IComparer comparer) {
96             _keyComparer = new CompatibleComparer( comparer, hashProvider);
97             Reset(capacity);
98         }
99 #pragma warning restore 618
100 
101         /// <devdoc>
102         /// <para>Creates an empty <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance with the specified
103         ///    initial capacity and using the default case-insensitive hash code provider
104         ///    and the default case-insensitive comparer.</para>
105         /// </devdoc>
NameObjectCollectionBase(int capacity)106         protected NameObjectCollectionBase(int capacity) {
107             _keyComparer = StringComparer.InvariantCultureIgnoreCase;
108             Reset(capacity);
109         }
110 
111         // Allow internal extenders to avoid creating the hashtable/arraylist.
NameObjectCollectionBase(DBNull dummy)112         internal NameObjectCollectionBase(DBNull dummy)
113         {
114         }
115 
116         //
117         // Serialization support
118         //
119 
120         /// <devdoc>
121         ///    <para>[To be supplied.]</para>
122         /// </devdoc>
NameObjectCollectionBase(SerializationInfo info, StreamingContext context)123         protected NameObjectCollectionBase(SerializationInfo info, StreamingContext context) {
124             _serializationInfo = info;
125         }
126 
127         /// <devdoc>
128         ///    <para>[To be supplied.]</para>
129         /// </devdoc>
130         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
131         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
GetObjectData(SerializationInfo info, StreamingContext context)132         public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
133             if (info == null)
134                 throw new ArgumentNullException("info");
135 
136             info.AddValue(ReadOnlyName, _readOnly);
137 
138 #pragma warning disable 618
139             // Maintain backward serialization compatibility if new APIs are not used.
140             if( _keyComparer == defaultComparer) {
141                 info.AddValue(HashCodeProviderName, CompatibleComparer.DefaultHashCodeProvider, typeof(IHashCodeProvider));
142                 info.AddValue(ComparerName, CompatibleComparer.DefaultComparer, typeof(IComparer));
143             }
144             else if(_keyComparer == null) {
145                 info.AddValue(HashCodeProviderName, null, typeof(IHashCodeProvider));
146                 info.AddValue(ComparerName, null, typeof(IComparer));
147             }
148             else if(_keyComparer is CompatibleComparer) {
149                 CompatibleComparer c = (CompatibleComparer)_keyComparer;
150                 info.AddValue(HashCodeProviderName, c.HashCodeProvider, typeof(IHashCodeProvider));
151                 info.AddValue(ComparerName, c.Comparer, typeof(IComparer));
152             }
153             else {
154                 info.AddValue(KeyComparerName, _keyComparer, typeof(IEqualityComparer));
155             }
156 #pragma warning restore 618
157 
158             int count = _entriesArray.Count;
159             info.AddValue(CountName, count);
160 
161             String[] keys = new String[count];
162             Object[] values = new Object[count];
163 
164             for (int i = 0; i < count; i++) {
165                 NameObjectEntry entry = (NameObjectEntry)_entriesArray[i];
166                 keys[i] = entry.Key;
167                 values[i] = entry.Value;
168             }
169 
170             info.AddValue(KeysName, keys, typeof(String[]));
171             info.AddValue(ValuesName, values, typeof(Object[]));
172             info.AddValue(VersionName, _version);
173         }
174 
175         /// <devdoc>
176         ///    <para>[To be supplied.]</para>
177         /// </devdoc>
OnDeserialization(Object sender)178         public virtual void OnDeserialization(Object sender) {
179             if (_keyComparer != null) {
180                 return;//Somebody had a dependency on this hashtable and fixed us up before the ObjectManager got to it.
181             }
182 
183             if (_serializationInfo == null)
184                 throw new SerializationException();
185 
186             SerializationInfo info = _serializationInfo;
187             _serializationInfo = null;
188 
189             bool readOnly = false;
190             int count = 0;
191             String[] keys = null;
192             Object[] values = null;
193 #pragma warning disable 618
194             IHashCodeProvider hashProvider = null;
195 #pragma warning restore 618
196             IComparer comparer = null;
197             bool hasVersion = false;
198             int serializedVersion = 0;
199 
200             SerializationInfoEnumerator enumerator = info.GetEnumerator();
201             while( enumerator.MoveNext())
202             {
203                 switch( enumerator.Name)
204                 {
205                     case ReadOnlyName:
206                         readOnly = info.GetBoolean(ReadOnlyName);;
207                         break;
208                     case HashCodeProviderName:
209 #pragma warning disable 618
210                         hashProvider = (IHashCodeProvider)info.GetValue(HashCodeProviderName, typeof(IHashCodeProvider));;
211 #pragma warning restore 618
212                         break;
213                     case ComparerName:
214                         comparer = (IComparer)info.GetValue(ComparerName, typeof(IComparer));
215                         break;
216                     case KeyComparerName:
217                         _keyComparer = (IEqualityComparer)info.GetValue(KeyComparerName, typeof(IEqualityComparer));
218                         break;
219                     case CountName:
220                         count = info.GetInt32(CountName);
221                         break;
222                     case KeysName:
223                         keys = (String[])info.GetValue(KeysName, typeof(String[]));
224                         break;
225                     case ValuesName:
226                         values = (Object[])info.GetValue(ValuesName, typeof(Object[]));
227                         break;
228                     case VersionName:
229                         hasVersion = true;
230                         serializedVersion = info.GetInt32(VersionName);
231                         break;
232                 }
233             }
234 
235             if( _keyComparer == null) {
236                 if(comparer == null || hashProvider == null)
237                 {
238                     throw new SerializationException();
239                 }
240                 else {
241                     // create a new key comparer for V1 Object
242                     _keyComparer = new CompatibleComparer(comparer, hashProvider);
243                 }
244             }
245 
246             if ( keys == null || values == null)
247                 throw new SerializationException();
248 
249             Reset(count);
250 
251             for (int i = 0; i < count; i++)
252                 BaseAdd(keys[i], values[i]);
253 
254             _readOnly = readOnly;  // after collection populated
255             if(hasVersion) {
256                 _version = serializedVersion;
257             }
258         }
259 
260         //
261         // Private helpers
262         //
263 
Reset()264         private void Reset() {
265             _entriesArray = new ArrayList();
266             _entriesTable = new Hashtable(_keyComparer);
267             _nullKeyEntry = null;
268             _version++;
269         }
270 
Reset(int capacity)271         private void Reset(int capacity) {
272             _entriesArray = new ArrayList(capacity);
273             _entriesTable = new Hashtable(capacity, _keyComparer);
274             _nullKeyEntry = null;
275             _version++;
276         }
277 
FindEntry(String key)278         private NameObjectEntry FindEntry(String key) {
279             if (key != null)
280                 return (NameObjectEntry)_entriesTable[key];
281             else
282                 return _nullKeyEntry;
283         }
284 
285         internal IEqualityComparer Comparer
286         {
287             get
288             {
289                 return _keyComparer;
290             }
291             set
292             {
293                 _keyComparer = value;
294             }
295 
296         }
297 
298 
299         /// <devdoc>
300         /// <para>Gets or sets a value indicating whether the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance is read-only.</para>
301         /// </devdoc>
302         protected bool IsReadOnly {
303             get { return _readOnly; }
304             set { _readOnly = value; }
305         }
306 
307         /// <devdoc>
308         /// <para>Gets a value indicating whether the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance contains entries whose
309         ///    keys are not <see langword='null'/>.</para>
310         /// </devdoc>
BaseHasKeys()311         protected bool BaseHasKeys() {
312             return (_entriesTable.Count > 0);  // any entries with keys?
313         }
314 
315         //
316         // Methods to add / remove entries
317         //
318 
319         /// <devdoc>
320         ///    <para>Adds an entry with the specified key and value into the
321         ///    <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
322         /// </devdoc>
BaseAdd(String name, Object value)323         protected void BaseAdd(String name, Object value) {
324             if (_readOnly)
325                 throw new NotSupportedException(SR.GetString(SR.CollectionReadOnly));
326 
327             NameObjectEntry entry = new NameObjectEntry(name, value);
328 
329             // insert entry into hashtable
330             if (name != null) {
331                 if (_entriesTable[name] == null)
332                     _entriesTable.Add(name, entry);
333             }
334             else { // null key -- special case -- hashtable doesn't like null keys
335                 if (_nullKeyEntry == null)
336                     _nullKeyEntry = entry;
337             }
338 
339             // add entry to the list
340             _entriesArray.Add(entry);
341 
342             _version++;
343         }
344 
345         /// <devdoc>
346         ///    <para>Removes the entries with the specified key from the
347         ///    <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
348         /// </devdoc>
BaseRemove(String name)349         protected void BaseRemove(String name) {
350             if (_readOnly)
351                 throw new NotSupportedException(SR.GetString(SR.CollectionReadOnly));
352 
353             if (name != null) {
354                 // remove from hashtable
355                 _entriesTable.Remove(name);
356 
357                 // remove from array
358                 for (int i = _entriesArray.Count-1; i >= 0; i--) {
359                     if (_keyComparer.Equals(name, BaseGetKey(i)))
360                         _entriesArray.RemoveAt(i);
361                 }
362             }
363             else { // null key -- special case
364                 // null out special 'null key' entry
365                 _nullKeyEntry = null;
366 
367                 // remove from array
368                 for (int i = _entriesArray.Count-1; i >= 0; i--) {
369                     if (BaseGetKey(i) == null)
370                         _entriesArray.RemoveAt(i);
371                 }
372             }
373 
374             _version++;
375         }
376 
377         /// <devdoc>
378         ///    <para> Removes the entry at the specified index of the
379         ///    <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
380         /// </devdoc>
BaseRemoveAt(int index)381         protected void BaseRemoveAt(int index) {
382             if (_readOnly)
383                 throw new NotSupportedException(SR.GetString(SR.CollectionReadOnly));
384 
385             String key = BaseGetKey(index);
386 
387             if (key != null) {
388                 // remove from hashtable
389                 _entriesTable.Remove(key);
390             }
391             else { // null key -- special case
392                 // null out special 'null key' entry
393                 _nullKeyEntry = null;
394             }
395 
396             // remove from array
397             _entriesArray.RemoveAt(index);
398 
399             _version++;
400         }
401 
402         /// <devdoc>
403         /// <para>Removes all entries from the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
404         /// </devdoc>
BaseClear()405         protected void BaseClear() {
406             if (_readOnly)
407                 throw new NotSupportedException(SR.GetString(SR.CollectionReadOnly));
408 
409             Reset();
410         }
411 
412         //
413         // Access by name
414         //
415 
416         /// <devdoc>
417         ///    <para>Gets the value of the first entry with the specified key from
418         ///       the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
419         /// </devdoc>
BaseGet(String name)420         protected Object BaseGet(String name) {
421             NameObjectEntry e = FindEntry(name);
422             return (e != null) ? e.Value : null;
423         }
424 
425         /// <devdoc>
426         /// <para>Sets the value of the first entry with the specified key in the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/>
427         /// instance, if found; otherwise, adds an entry with the specified key and value
428         /// into the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/>
429         /// instance.</para>
430         /// </devdoc>
BaseSet(String name, Object value)431         protected void BaseSet(String name, Object value) {
432             if (_readOnly)
433                 throw new NotSupportedException(SR.GetString(SR.CollectionReadOnly));
434 
435             NameObjectEntry entry = FindEntry(name);
436             if (entry != null) {
437                 entry.Value = value;
438                 _version++;
439             }
440             else {
441                 BaseAdd(name, value);
442             }
443         }
444 
445         //
446         // Access by index
447         //
448 
449         /// <devdoc>
450         ///    <para>Gets the value of the entry at the specified index of
451         ///       the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
452         /// </devdoc>
BaseGet(int index)453         protected Object BaseGet(int index) {
454             NameObjectEntry entry = (NameObjectEntry)_entriesArray[index];
455             return entry.Value;
456         }
457 
458         /// <devdoc>
459         ///    <para>Gets the key of the entry at the specified index of the
460         ///    <see cref='System.Collections.Specialized.NameObjectCollectionBase'/>
461         ///    instance.</para>
462         /// </devdoc>
BaseGetKey(int index)463         protected String BaseGetKey(int index) {
464             NameObjectEntry entry = (NameObjectEntry)_entriesArray[index];
465             return entry.Key;
466         }
467 
468         /// <devdoc>
469         ///    <para>Sets the value of the entry at the specified index of
470         ///       the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
471         /// </devdoc>
BaseSet(int index, Object value)472         protected void BaseSet(int index, Object value) {
473             if (_readOnly)
474                 throw new NotSupportedException(SR.GetString(SR.CollectionReadOnly));
475 
476             NameObjectEntry entry = (NameObjectEntry)_entriesArray[index];
477             entry.Value = value;
478             _version++;
479         }
480 
481         //
482         // ICollection implementation
483         //
484 
485         /// <devdoc>
486         /// <para>Returns an enumerator that can iterate through the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/>.</para>
487         /// </devdoc>
GetEnumerator()488         public virtual IEnumerator GetEnumerator() {
489             return new NameObjectKeysEnumerator(this);
490         }
491 
492         /// <devdoc>
493         /// <para>Gets the number of key-and-value pairs in the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
494         /// </devdoc>
495         public virtual int Count {
496             get {
497                 return _entriesArray.Count;
498             }
499         }
500 
ICollection.CopyTo(Array array, int index)501         void ICollection.CopyTo(Array array, int index) {
502             if (array==null) {
503                 throw new ArgumentNullException("array");
504             }
505 
506             if (array.Rank != 1) {
507                 throw new ArgumentException(SR.GetString(SR.Arg_MultiRank));
508             }
509 
510             if (index < 0) {
511                 throw new ArgumentOutOfRangeException("index",SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture)) );
512             }
513 
514             if (array.Length - index < _entriesArray.Count) {
515                 throw new ArgumentException(SR.GetString(SR.Arg_InsufficientSpace));
516             }
517 
518             for (IEnumerator e = this.GetEnumerator(); e.MoveNext();)
519                 array.SetValue(e.Current, index++);
520         }
521 
522         Object ICollection.SyncRoot {
523             get {
524                 if( _syncRoot == null) {
525                     System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null);
526                 }
527                 return _syncRoot;
528             }
529         }
530 
531         bool ICollection.IsSynchronized {
532             get { return false; }
533         }
534 
535         //
536         //  Helper methods to get arrays of keys and values
537         //
538 
539         /// <devdoc>
540         /// <para>Returns a <see cref='System.String' qualify='true'/> array containing all the keys in the
541         /// <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
542         /// </devdoc>
BaseGetAllKeys()543         protected String[] BaseGetAllKeys() {
544             int n = _entriesArray.Count;
545             String[] allKeys = new String[n];
546 
547             for (int i = 0; i < n; i++)
548                 allKeys[i] = BaseGetKey(i);
549 
550             return allKeys;
551         }
552 
553         /// <devdoc>
554         /// <para>Returns an <see cref='System.Object' qualify='true'/> array containing all the values in the
555         /// <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
556         /// </devdoc>
BaseGetAllValues()557         protected Object[] BaseGetAllValues() {
558             int n = _entriesArray.Count;
559             Object[] allValues = new Object[n];
560 
561             for (int i = 0; i < n; i++)
562                 allValues[i] = BaseGet(i);
563 
564             return allValues;
565         }
566 
567         /// <devdoc>
568         ///    <para>Returns an array of the specified type containing
569         ///       all the values in the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
570         /// </devdoc>
BaseGetAllValues(Type type)571         protected object[] BaseGetAllValues(Type type) {
572             int n = _entriesArray.Count;
573             if (type == null) {
574                 throw new ArgumentNullException("type");
575             }
576             object[] allValues = (object[]) SecurityUtils.ArrayCreateInstance(type, n);
577 
578             for (int i = 0; i < n; i++) {
579                 allValues[i] = BaseGet(i);
580             }
581 
582             return allValues;
583         }
584 
585         //
586         // Keys propetry
587         //
588 
589         /// <devdoc>
590         /// <para>Returns a <see cref='System.Collections.Specialized.NameObjectCollectionBase.KeysCollection'/> instance containing
591         ///    all the keys in the <see cref='System.Collections.Specialized.NameObjectCollectionBase'/> instance.</para>
592         /// </devdoc>
593         public virtual KeysCollection Keys {
594             get {
595                 if (_keys == null)
596                     _keys = new KeysCollection(this);
597                 return _keys;
598             }
599         }
600 
601         //
602         // Simple entry class to allow substitution of values and indexed access to keys
603         //
604 
605         internal class NameObjectEntry {
606 
NameObjectEntry(String name, Object value)607             internal NameObjectEntry(String name, Object value) {
608                 Key = name;
609                 Value = value;
610             }
611 
612             internal String Key;
613             internal Object Value;
614         }
615 
616         //
617         // Enumerator over keys of NameObjectCollection
618         //
619 
620         [Serializable()]
621         internal class NameObjectKeysEnumerator : IEnumerator {
622             private int _pos;
623             private NameObjectCollectionBase _coll;
624             private int _version;
625 
NameObjectKeysEnumerator(NameObjectCollectionBase coll)626             internal NameObjectKeysEnumerator(NameObjectCollectionBase coll) {
627                 _coll = coll;
628                 _version = _coll._version;
629                 _pos = -1;
630             }
631 
MoveNext()632             public bool MoveNext() {
633                 if ( _version != _coll._version)
634                     throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumFailedVersion));
635 
636                 if( _pos < _coll.Count - 1) {
637                     _pos++;
638                     return true;
639                 }
640                 else {
641                     _pos = _coll.Count;
642                     return false;
643                 }
644 
645             }
646 
Reset()647             public void Reset() {
648                 if (_version != _coll._version)
649                     throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumFailedVersion));
650                 _pos = -1;
651             }
652 
653             public Object Current {
654                 get {
655                     if(_pos >= 0 && _pos < _coll.Count) {
656                         return _coll.BaseGetKey(_pos);
657                     }
658                     else {
659                         throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumOpCantHappen));
660                     }
661                 }
662             }
663         }
664 
665         //
666         // Keys collection
667         //
668 
669         /// <devdoc>
670         /// <para>Represents a collection of the <see cref='System.String' qualify='true'/> keys of a collection.</para>
671         /// </devdoc>
672         [Serializable()]
673         public class KeysCollection : ICollection {
674 
675             private NameObjectCollectionBase _coll;
676 
KeysCollection(NameObjectCollectionBase coll)677             internal KeysCollection(NameObjectCollectionBase coll) {
678                 _coll = coll;
679             }
680 
681             // Indexed access
682 
683             /// <devdoc>
684             ///    <para> Gets the key at the specified index of the collection.</para>
685             /// </devdoc>
Get(int index)686             public virtual String Get(int index) {
687                 return _coll.BaseGetKey(index);
688             }
689 
690             /// <devdoc>
691             ///    <para>Represents the entry at the specified index of the collection.</para>
692             /// </devdoc>
693             public String this[int index] {
694                 get {
695                     return Get(index);
696                 }
697             }
698 
699             // ICollection implementation
700 
701             /// <devdoc>
702             ///    <para>Returns an enumerator that can iterate through the
703             ///    <see cref='System.Collections.Specialized.NameObjectCollectionBase.KeysCollection'/>.</para>
704             /// </devdoc>
GetEnumerator()705             public IEnumerator GetEnumerator() {
706                 return new NameObjectKeysEnumerator(_coll);
707             }
708 
709             /// <devdoc>
710             /// <para>Gets the number of keys in the <see cref='System.Collections.Specialized.NameObjectCollectionBase.KeysCollection'/>.</para>
711             /// </devdoc>
712             public int Count {
713                 get {
714                     return _coll.Count;
715                 }
716             }
717 
ICollection.CopyTo(Array array, int index)718             void ICollection.CopyTo(Array array, int index) {
719                 if (array==null) {
720                     throw new ArgumentNullException("array");
721                 }
722 
723                 if (array.Rank != 1) {
724                     throw new ArgumentException(SR.GetString(SR.Arg_MultiRank));
725                 }
726 
727                 if (index < 0) {
728                     throw new ArgumentOutOfRangeException("index",SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture)) );
729                 }
730 
731                 if (array.Length - index < _coll.Count) {
732                     throw new ArgumentException(SR.GetString(SR.Arg_InsufficientSpace));
733                 }
734 
735                 for (IEnumerator e = this.GetEnumerator(); e.MoveNext();)
736                     array.SetValue(e.Current, index++);
737             }
738 
739             Object ICollection.SyncRoot {
740                 get { return ((ICollection)_coll).SyncRoot; }
741             }
742 
743 
744             bool ICollection.IsSynchronized {
745                 get { return false; }
746             }
747         }
748     }
749 
750     [Serializable()]
751     internal class CompatibleComparer: IEqualityComparer  {
752         IComparer _comparer;
753         static volatile IComparer defaultComparer;
754         // Needed for compatability
755 #pragma warning disable 618
756         IHashCodeProvider _hcp;
757 
758         static volatile IHashCodeProvider defaultHashProvider;
759 
CompatibleComparer(IComparer comparer, IHashCodeProvider hashCodeProvider)760         internal CompatibleComparer(IComparer comparer, IHashCodeProvider hashCodeProvider) {
761             _comparer = comparer;
762             _hcp = hashCodeProvider;
763         }
764 #pragma warning restore 618
765 
Equals(Object a, Object b)766         public new bool Equals(Object a, Object b) {
767             if (a == b) return true;
768             if (a == null || b == null) return false;
769 
770             // We must call Compare or CompareTo method
771             // to make sure everything is fine, but the
772             // guideline is that Equals should never throw.
773             // So we need to ignore ArgumentException (note this
774             // is the exception we should get if two objects are not
775             // comparable.)
776 
777             try {
778                 if (_comparer != null)
779                     return (_comparer.Compare(a,b) == 0);
780 
781                 IComparable ia = a as IComparable;
782                 if (ia != null)
783                     return (ia.CompareTo(b) ==0);
784             }
785             catch(ArgumentException) {
786                 return false;
787             }
788 
789             return a.Equals(b);
790         }
791 
GetHashCode(Object obj)792         public int GetHashCode(Object obj) {
793             if( obj == null) {
794                 throw new ArgumentNullException("obj");
795             }
796 
797             if (_hcp != null)
798                 return _hcp.GetHashCode(obj);
799             return obj.GetHashCode();
800         }
801 
802         public IComparer Comparer {
803             get {
804                 return _comparer;
805             }
806         }
807 
808 #pragma warning disable 618
809         public IHashCodeProvider HashCodeProvider {
810             get {
811                 return _hcp;
812             }
813         }
814 #pragma warning restore 618
815 
816         public static IComparer DefaultComparer {
817             get {
818                 if( defaultComparer == null) {
819                     defaultComparer = new CaseInsensitiveComparer(CultureInfo.InvariantCulture);
820                 }
821                 return defaultComparer;
822             }
823         }
824 
825 #pragma warning disable 618
826         public static IHashCodeProvider DefaultHashCodeProvider {
827             get {
828                 if( defaultHashProvider == null) {
829                     defaultHashProvider = new CaseInsensitiveHashCodeProvider(CultureInfo.InvariantCulture);
830                 }
831                 return defaultHashProvider;
832             }
833         }
834 #pragma warning restore 618
835     }
836 }
837