Recipe4.10.Replacing the Hashtable with Its Generic Counterpart


Recipe 4.10. Replacing the Hashtable with Its Generic Counterpart

Problem

You want to enhance the performance of your application as well as make the code easier to work with by replacing all Hashtable objects with the generic version. This is imperative when you find that structures or other value types are being stored in these data structures, resulting in boxing/unboxing operations.

Solution

Replace all occurrences of the System.Collections.Hashtable class with the faster generic System.Collections.Generic.Dictionary class.

Here is a simple example of using a System.Collections.Hashtable object:

 public static void UseNonGenericHashtable() {      // Create and populate a Hashtable.     Hashtable numbers = new Hashtable();      numbers.Add(1, "one");    // Causes a boxing operation to occur for the key      numbers.Add(2, "two");    // Causes a boxing operation to occur for the key     // Display all key/value pairs in the Hashtable.     // Causes an unboxing operation to occur on each iteration for the key     foreach (DictionaryEntry de in numbers)     {         Console.WriteLine("Key: " + de.Key + "\tValue: " + de.Value);      }     numbers.Clear(); } 

Here is that same code using a System.Collections.Generic.Dictionary<T,U> object:

 public static void UseGenericDictionary() {     // Create and populate a Dictionary.     Dictionary<int, string> numbers = new Dictionary<int, string>();     numbers.Add(1, "one");     numbers.Add(2, "two");     // Display all key/value pairs in the Dictionary.     foreach (KeyValuePair<int, string> kvp in numbers)     {         Console.WriteLine("Key: " + kvp.Key + "\tValue: " + kvp.Value);      }     numbers.Clear(); } 

Discussion

For simple implementations of the Hashtable in your application, this substitution should be quite easy. However, there are some things to watch out for. For example, the generic Dictionary class does not implement the ICloneable interface, while the Hashtable class does.

Table 4-2 shows the equivalent members that are implemented in both classes.

Table 4-2. Equivalent members in the Hashtable and the generic Dictionary classes

Members in the Hashtable class

Equivalent members in the generic Dictionary class

N/A

Comparer property

Count property

Count property

IsFixedSize property

((IDictionary)myDict).IsFixedSize

IsReadOnly property

((IDictionary)myDict).IsReadOnly

IsSynchronized property

((IDictionary)myDict).IsSynchronized

Item property

Item property

Keys property

Keys property

SyncRoot property

((IDictionary)myDict).SyncRoot

Values property

Values property

Add method

Add method

Clear method

Clear method

Clone method

Use overloaded constructor which accepts an IDictionary<T,U> type

Contains method

ContainsKey method

ContainsKey method

ContainsKey method

ContainsValue method

ContainsValue method

CopyTo method

((ICollection)myDict).CopyTo(arr,0)

Remove method

Remove method

Synchronized static method

lock(myDictionary.SyncRoot) {…}

N/A

TRyGetValue method


In several cases within Table 4-2, there is not a one-to-one correlation between the members of a Hashtable and the members of the generic Dictionary class. Starting with the properties, notice that only the Count, Keys, Values, and Item properties are present in both classes. To make up for the missing properties in the Dictionary class, you can perform a cast to an IDictionary. The following code shows how to use these casts to get at the missing properties:

 Dictionary<int, string> numbers = new Dictionary<int, string>(); Console.WriteLine(((IDictionary)numbers).IsReadOnly); Console.WriteLine(((IDictionary)numbers).IsFixedSize); Console.WriteLine(((IDictionary)numbers).IsSynchronized); Console.WriteLine(((IDictionary)numbers).SyncRoot); 

Note that due to the absence of code to be able to return a synchronized version of a generic Dictionary, the IsSynchronized property will always return false. The SyncRoot property will always return the same object on which it is called. Essentially, this property returns the this pointer. Microsoft has decided to remove the ability to create a synchronous wrapper from any of the generic collection classes.

Instead, they recommend using the lock keyword to lock the entire collection or another type of synchronization object that suits your needs.

Since the Clone method is also missing from the generic Dictionary class (due to the fact that this class does not implement the ICloneable interface), you can instead use the overloaded constructor, which accepts an IDictionary<T,U> type:

 // Create and populate a Dictionary. Dictionary<int, string> numbers = new Dictionary<int, string>(); numbers.Add(1, "one"); numbers.Add(2, "two"); // Display all key/value pairs in the original Dictionary. foreach (KeyValuePair<int, string> kvp in numbers) {     Console.WriteLine("Original Key: " + kvp.Key + "\tValue: " + kvp.Value); } // Clone the Dictionary object. Dictionary<int, string> clonedNumbers = new Dictionary<int, string>(numbers); // Display all key/value pairs in the cloned Dictionary. foreach (KeyValuePair<int, string> kvp in numbers) {     Console.WriteLine("Cloned Key: " + kvp.Key + "\tValue: " + kvp.Value); } 

There are two more methods that are missing from the Dictionary class, the Contains and CopyTo methods. The Contains method is easy to reproduce in the Dictionary class. In the Hashtable class, the Contains method and the ContainsKey method both exhibit the same behavior, therefore you can simply use the ContainsKey method of the Dictionary class to simulate the Contains method of the Hashtable class:

 // Create and populate a Dictionary. Dictionary<int, string> numbers = new Dictionary<int, string>(); numbers.Add(1, "one"); numbers.Add(2, "two"); Console.WriteLine("numbers.ContainsKey(1) == " + numbers.ContainsKey(1));  Console.WriteLine("numbers.ContainsKey(3) == " + numbers.ContainsKey(3));  

The CopyTo method is also easy to simulate in the Dictionary class, but it involves a little more work:

 // Create and populate a Dictionary. Dictionary<int, string> numbers = new Dictionary<int, string>(); numbers.Add(1, "one"); numbers.Add(2, "two"); // Display all key/value pairs in the Dictionary. foreach (KeyValuePair<int, string> kvp in numbers) {     Console.WriteLine("Key: " + kvp.Key + "\tValue: " + kvp.Value); } // Create object array to hold copied information from Dictionary object.  KeyValuePair<int, string>[] objs = new KeyValuePair<int, string>[numbers.Count]; // Calling CopyTo on a Dictionary // Copies all KeyValuePair objects in Dictionary object to objs[] ((IDictionary)numbers).CopyTo(objs, 0); // Display all key/value pairs in the objs[]. foreach (KeyValuePair<int, string> kvp in objs) {     Console.WriteLine("Key: " + kvp.Key + "\tValue: " + kvp.Value); } 

Calling CopyTo on the Dictionary object involves setting up an array of KeyValuePair<T,U> objects, which will end up holding all the KeyValuePair<T,U> objects within the Dictionary object after the CopyTo method is called. Next, the numbers Dictionary object is cast to an IDictionary type so that the CopyTo method may be called. Once the CopyTo method is called, the objs array will contain copies of all the KeyValuePair<T,U> objects that are in the original numbers object. Note that iteration of the objs array, using a foreach loop, is done in the same fashion as with the numbers object.

See Also

See the "System.Collections.Hashtable Class" and "System.Collections.Generic.Dictionary Class" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net