Build a C# DictionaryHashList Collection Object
By Peter A. Bromberg, Ph.D.

Peter Bromberg

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

 

 

Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.