|
System.Collections provides a number of useful collection types including ArrayList,
BitArray, Hashtable, Queue, SortedList and Stack, among other classes designed
to be derived from in custom collection implementations.
One of the animals missing from the "collection" is one that
provides the capabilities of the SortedList (key - value pairs that are
not only sorted by key but also accessible by index) , but can also present
the data by index in the order it was inserted (not the "Sorted" order).
This animal, like the ArrayList and HashTable, should also permit the
values to be objects. There have been a number of implementations of
what is called the "HashList" in the JAVA space, but only one
I found in the .NET domain - one written by Mike
McPhail here. Unfotunately for Mike, his search didn't permit him
to find the NameObjectCollectionBase class, which would have made his
job a lot easier (his work still stands on its own merits as an excellent
example of "using your noodle", however).
NameObjectCollectionBase provides the abstract base class for a collection
of associated String keys
and Object values that
can be accessed either with the key or with the index, and lives in the System.Collections.Specialized namespace.
Its
underlying structure is a Hashtable, but unlike the Hashtable, where index iteration
is unpredictable because of the way HashCodes are generated, you can iterate
over this class and each item will appear in its original
insertion order. In addition, a null reference is allowed as either
a key or a value. Additional private fields in NameObjectCollectionBase
include:
private IComparer _comparer;
private ArrayList _entriesArray;
private Hashtable _entriesTable;
private IHashCodeProvider _hashProvider;
private KeysCollection _keys;
private NameObjectEntry _nullKeyEntry;
private bool _readOnly;
private SerializationInfo _serializationInfo;
private int _version;
What I've done here is create a class derived from NameObjectCollectionBase
that does everything we want. (The implementation is no great success
story, because 90% of the code actually comes directly out the nice example
code provide by the MS Gurus who wrote the Visual Studio.NET Documentation). The only thing NameObjectCollectionBase does not provide us with is the ability to have keys, as well as values, be Objects. However, in practice, I believe few developers really would ever need this feature.
Here's the code for my DictionaryHashList class:
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
namespace System.Collections.Specialized
{
public class DictionaryHashList : NameObjectCollectionBase
{
private DictionaryEntry _de = new DictionaryEntry();
// Creates an empty collection.
public DictionaryHashList()
{
}
// Adds elements from an IDictionary into the new collection.
public DictionaryHashList( IDictionary d, Boolean bReadOnly )
{
foreach ( DictionaryEntry de in d )
{
this.BaseAdd( (String) de.Key, de.Value );
}
this.IsReadOnly = bReadOnly;
}
// Gets a key-and-value pair (DictionaryEntry) using an index.
public DictionaryEntry this[ int index ]
{
get
{
_de.Key = this.BaseGetKey(index);
_de.Value = this.BaseGet(index);
return( _de );
}
}
// Gets or sets the value associated with the specified key.
public Object this[ String key ]
{
get
{
return( this.BaseGet( key ) );
}
set
{
this.BaseSet( key, value );
}
}
// Gets a String array that contains all the keys in the collection.
public String[] AllKeys
{
get
{
return( this.BaseGetAllKeys() );
}
}
// Gets an Object array that contains all the values in the collection.
public Array AllValues
{
get
{
return( this.BaseGetAllValues() );
}
}
// Gets a String array that contains all the values in the collection.
public String[] AllStringValues
{
get
{
return( (String[]) this.BaseGetAllValues( Type.GetType( "System.String" ) ) );
}
}
// Gets a value indicating if the collection contains keys that are not null.
public Boolean HasKeys
{
get
{
return( this.BaseHasKeys() );
}
}
// Adds an entry to the collection.
public void Add( String key, Object value )
{
this.BaseAdd( key, value );
}
// Removes an entry with the specified key from the collection.
public void Remove( String key )
{
this.BaseRemove( key );
}
// Removes an entry in the specified index from the collection.
public void Remove( int index )
{
this.BaseRemoveAt( index );
}
// Clears all the elements in the collection.
public void Clear()
{
this.BaseClear();
}
}
} |
Most all of the above, combined with the comments in the
code, should be self-explanatory. When you download the solution below,
the Windows Forms Test harness will enable you to test this. You can
add as many items to a DictionaryHashList as you want. Then, you can
pull back a key and value by entering the key, or by entering the integer
index of an item. Finally, the button on the bottom lets you see that
the index order of your collection is indeed, exactly the same as the
order you entered your items! In addition, the sample code shows how
to use IEnumerator along with a DictionaryEntry , a struct type which
is returned
by the IDictionaryEnumerator.Entry method.
Download the code that accompanies this article
|