Generics, Dictionaries, and More
(Page 1 of 4 )
In this conclusion to a three-part series on generics in C#, you'll learn how to replace hash table objects with their generic counterparts, how to handle constraining type arguments, and more. This article is excerpted from chapter four of the
C# 3.0 Cookbook, Third Edition, written by Jay Hilyard and Stephen Teilhet (O'Reilly, 2008; ISBN: 059651610X). Copyright © 2008 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.
4.9 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.
Solution
Replace all occurrences of the System.Collections.Hashtable class with the type-safe generic System.Collections.Generic.Dictionary class.
Here is a simple example of using aSystem.Collections.Hashtableobject:
public static void UseNonGenericHashtable()
{
Console.WriteLine("\r\nUseNonGenericHashtable");
// Create and populate a Hashtable
Hashtable numbers = new Hashtable()
{ {1, "one"},"one"}, // Causes a boxing operation to occur for the key
{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);
}
Console.WriteLine(numbers.IsReadOnly);
Console.WriteLine(numbers.IsFixedSize);
Console.WriteLine(numbers.IsSynchronized);
Console.WriteLine(numbers.SyncRoot);
numbers.Clear();
}
Here is that same code using aSystem.Collections.Generic.Dictionary<T,U>object:
public static void UseGenericDictionary()
{
Console.WriteLine("\r\nUseGenericDictionary");
// Create and populate a Dictionary
Dictionary<int, string> numbers = new Dictionary<int, string>()
{ { 1, "one" }, { 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);
}
Console.WriteLine(((IDictionary)numbers).IsReadOnly);
Console.WriteLine(((IDictionary)numbers).IsFixedSize);
Console.WriteLine(((IDictionary)numbers).IsSynchronized);
Console.WriteLine(((IDictionary)numbers).SyncRoot);
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-3 shows the equivalent members that are implemented in both classes.
Table 4-3. Equivalent members in the Hashtable and the generic Dictionary classes
| Members in the Hashtable class | Equivalent members in the generic Dictionary class |
| N/A | Comparerproperty |
| Countproperty | Countproperty |
| IsFixedSizeproperty | ((IDictionary)myDict).IsFixedSize |
| IsReadOnlyproperty | ((IDictionary)myDict).IsReadOnly |
| IsSynchronizedproperty | ((IDictionary)myDict).IsSynchronized |
| Itemproperty | Itemproperty |
| Keysproperty | Keysproperty |
| SyncRootproperty | ((IDictionary)myDict).SyncRoot |
| Valuesproperty | Valuesproperty |
| Addmethod | Addmethod |
| Clearmethod | Clearmethod |
| Clonemethod | Use overloaded constructor, which accepts anIDictionarytype |
| Containsmethod | ContainsKeymethod |
| ContainsKeymethod | ContainsKeymethod |
| ContainsValuemethod | ContainsValuemethod |
| CopyTomethod | ((ICollection)myDict).CopyTo(arr,0) |
| Removemethod | Removemethod |
| Synchronizedstatic method | lock(myDictionary.SyncRoot) {...} |
| N/A | TryGetValuemethod |
In several cases within Table 4-3, there is not a one-to-one correlation between the members of aHashtableand the members of the genericDictionaryclass. Starting with the properties, notice that only theCount,Keys,Values, andItemproperties are present in both classes. To make up for the missing properties in theDictionaryclass, you can perform a cast to anIDictionary. 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 genericDictionary, theIsSynchronizedproperty will always returnfalse. TheSyncRootproperty will always return the same object on which it is called. Essentially, this property returns thethispointer. Microsoft has decided to remove the ability to create a synchronous wrapper from any of the generic collection classes.
Instead, they recommend using thelockkeyword to lock the entire collection or another type of synchronization object that suits your needs.
Since theClonemethod is also missing from the genericDictionaryclass (due to the fact that this class does not implement theICloneableinterface), you can instead use the overloaded constructor, which accepts anIDictionary<T,U>type:
// Create and populate a Dictionary
Dictionary<int, string> numbers = new
Dictionary<int, string>()
{ { 1, "one" }, { 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 theDictionaryclass, theContainsandCopyTo methods. TheContainsmethod is easy to reproduce in theDictionaryclass. In theHashtableclass, theContainsmethod and theContainsKey method both exhibit the same behavior; therefore, you can simply use theContainsKeymethod of theDictionaryclass to simulate theContains method of theHashtable class:
// Create and populate a Dictionary
Dictionary<int, string> numbers =
new Dictionary<int, string>()
{ { 1, "one" }, { 2, "two" } };
Console.WriteLine("numbers.ContainsKey(1) == " + numbers.ContainsKey(1));
Console.WriteLine("numbers.ContainsKey(3) == " + numbers.ContainsKey(3));
TheCopyTomethod is also easy to simulate in theDictionaryclass, but it involves a little more work:
// Create and populate a Dictionary
Dictionary<int, string> numbers =
new Dictionary<int, string>()
{ { 1, "one" }, { 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);
}
CallingCopyToon theDictionaryobject involves setting up an array ofKeyValuePair<T,U>objects, which will end up holding all theKeyValuePair<T,U>objects within theDictionaryobject after theCopyTo method is called. Next, thenumbers Dictionaryobject is cast to anIDictionarytype so that theCopyTomethod may be called. Once theCopyTomethod is called, theobjsarray will contain copies of all theKeyValuePair<T,U>objects that are in the originalnumbersobject. Note that iteration of theobjsarray, using aforeachloop, is done in the same fashion as with thenumbersobject.
See Also
The “System.Collections.Hashtable Class” and “System.Collections.Generic.Dictionary Class” topics in the MSDN documentation.
Next: 4.10 Using foreach with Generic Dictionary Types >>
More C# Articles
More By O'Reilly Media
|
This article is excerpted from chapter four of the C# 3.0 Cookbook, Third Edition, written by Jay Hilyard and Stephen Teilhet (O'Reilly, 2008; ISBN: 059651610X). Check it out today at your favorite bookstore. Buy this book now.
|
|