4.9 利用对应的泛型替换Hashtable
问题
你希望通过用其泛型版本替换所有的Hashtable对象来增强应用程序的性能,并使得代码更容易处理。
解决方案
利用类型安全的泛型类System.Collections.Generic.Dictionary替换出现的所有System.Collections.Hashtable类。
下面给出了一个使用System.Collections.Hashtable对象的简单示例:
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(); } |
下面给出了使用System.Collections.Generic.Dictionary<T,U>对象的相同代码:
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(); }
|
讨论
对于应用程序中Hashtable的简单实现,这种替换应该相当容易。不过,有一些事情要注意。例如,泛型类Dictionary没有实现ICloneable接口,而Hashtable类则实现了该接口。
表4-3显示了两个类中同时实现的等价成员。
表4-3:Hashtable和泛型类Dictionary中的等价成员
Hashtable类中的成员 |
泛型类Dictionary中的等价成员 |
无 |
Comparer属性 |
Count属性 |
Count属性 |
IsFixedSize属性 |
((IDictionary)myDict).IsFixedSize |
IsReadOnly属性 |
((IDictionary)myDict).IsReadOnly |
IsSynchronized属性 |
((IDictionary)myDict).IsSynchronized |
Item属性 |
Item属性 |
Keys属性 |
Keys属性 |
SyncRoot属性 |
((IDictionary)myDict).SyncRoot |
Values属性 |
Values属性 |
Add方法 |
Add方法 |
Clear方法 |
Clear方法 |
Clone方法 |
使用重载的构造函数,它接受一个IDictionary<T,U>类型 |
Contains方法 |
ContainsKey方法 |
ContainsKey方法 |
ContainsKey方法 |
ContainsValue方法 |
ContainsValue方法 |
CopyTo方法 |
((ICollection)myDict).CopyTo(arr,0) |
表4-3:Hashtable和泛型类Dictionary中的等价成员(续)
Hashtable类中的成员 |
泛型类Dictionary中的等价成员 |
Remove方法 |
Remove方法 |
Synchronized静态方法 |
lock(myDictionary.SyncRoot) {...} |
无 |
TryGetValue方法 |
在表4-3中,有些情况下在Hashtable的成员与泛型类Dictionary的成员之间没有一对一的关系。从属性开始,注意只有Count、Keys、Values和Item这些属性同时存在于两个类中。为了弥补Dictionary类中缺失的属性,可以强制转换到IDictionary。下面的代码显示了如何使用这些强制转换获得缺失的属性:
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);
|
注意: 由于没有代码能够返回泛型Dictionary的同步版本,IsSynchronized属性将总是返回false。SyncRoot属性将总是返回调用它的相同对象。实质上,这个属性返回this指针。Microsoft决定取消从任何泛型集合类创建同步包装器的能力。
作为替代,他们建议使用lock关键字来锁定整个集合或者适合你的需要的另一类同步对象。
由于泛型类Dictionary中也缺失Clone方法(这是由于这个类没有实现ICloneable接口),可以代之以使用重载的构造函数,它接受一个IDictionary<T,U>类型:
// 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); } |
Dictionary类中还缺失了另外两个方法:Contains和CopyTo方法。可以很容易地在Dictionary类中复制Contains方法。在Hashtable类中,Contains方法和ContainsKey方法都会展示相同的行为;因此,可以简单地使用Dictionary类的ContainsKey方法来模拟Hashtable类的Contains方法:
// 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)); |
在Dictionary类中也很容易模拟CopyTo方法,但是它需要做更多一点的工作:
// 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); } |
对Dictionary对象调用CopyTo方法涉及建立一个KeyValuePair<T,U>对象的数组,在调用CopyTo方法之后,该数组最终将用于保存Dictionary对象内的所有KeyValuePair<T,U>对象。接下来,将Dictionary对象numbers强制转换成IDictionary类型,以便可以调用CopyTo方法。一旦调用了CopyTo方法,objs数组将包含原始numbers对象中的所有KeyValuePair<T,U>对象的副本。注意:使用foreach循环以与numbers对象相同的方式迭代objs数组。
参考
MSDN文档中的"System.Collections.Hashtable类"和"System.Collections.Generic. Dictionary类"主题。