毫无疑问,范型最典型的应用莫过于范型集合了。在 .NET 2.0 中提供了已有集合类和接口的范型版本,它们位于 System.Collections.Generic 命名空间中。
.NET 2.0 中新的范型集合类并不是简单的在已有非范型集合类的设计上多加了个范型参数 T 而已。新的范型集合类的设计充分吸收了已有设计中的合理之处并摒弃了一些不甚合理之处,同时引入了新的针对范型的设计。所以,新的范型类和接口的设计应该更加合理和有效,不过 .NET 程序员则需要花些时间学习新的设计并了解与已有设计有什么样的不同,以及在将代码从非范型集合移植到范型集合时可能会出现的兼容性问题。
下面是范型集合和已有非范型集合的对照表(不全):
非范型接口 |
范型接口 |
非范型类 |
范型类 |
IEnumerator |
IEnumerator<T>
|
ArrayList |
List<T>
|
IEnumerable |
IEnumerable<T>
|
Stack |
Stack<T>
|
ICollection |
ICollection<T>
|
Queue |
Queue<T>
|
IList |
IList<T>
|
DictionaryEntry |
KeyValuePair<K, V>
|
IDictionary |
IDictionary<T>
|
Hashtable |
Dictionary<K, V>
|
IComparable |
IComparable<T>
|
Comparer |
Comparer<T>
|
IComparer |
IComparer<T>
|
|
|
可以看到,部分类的名字做了修改,例如 ArrayList 现在改为 List<T>
前面说过,新的范型集合接口/类和以前的非范型版本相比有较大的设计改变,下面我们来看看这些变化。
IEnumerator/IEnumerator<T>
l IEnumerator<T>
l C# 2.0 Iterators 提供了自动生成枚举器的方法,编译器自动为指定的类实现 IEnumerator 接口和 IEnumerator<T>
ICollection<T>
除此之外,ICollection<T>
l IsReadOnly,用于判断集合是否是只读的。
l Add/Remove/Clear,用于对集合元素进行管理。这些方法对列表和字典都是有效的。
l Contains,用于判断集合中是否包含指定的值。
另外,对于一些不需要更改集合的使用情景来说,提供一个类似 IReadOnlyCollection<T>
刚才提到,IList<T>
List<T>
List<T>
IDictionary<K, V>
Hashtable map = ...;
if (map[“s1”] == null) { // 如果是范型版本将抛出异常而不是返回null
...
}
这一问题反映了设计者在最初设计 Hashtable 类的时候考虑的并不是很周到——使用了魔术值 null,既可以是指键不存在的情况,也可以是键存在而值为 null 的情况,而这一点对范型是不成立的。另外,从 Design By Contract 的角度讲,当指定键值不存在时,抛出异常是很自然的事情(与是否使用范型无关),就像数组越界一样。估计原设计者主要是从性能角度考虑才使用了 null 而不是异常处理。
这几个接口/类用于比较和排序。IComparable<T>
public Hashtable(IHashCodeProvider hcp, IComparer comparer);
其中 IHashCodeProvider 和 IComparer 两个参数必须匹配(例如都使用 InvariantCultureIgnoreCase),否则结果会不正确。为了让程序员能够快速的编写出正确的代码,现在的 IComparer<T>
public Dictionary(IComparer<K, V>
Comparer<K, V>
另外,.NET 2.0 中新添加了一个字符串比较类——StringComparer,位于 System 命名空间。StringComparer 不是一个范型类,不过它实现了 IComparer<string>
Dictionary<string, int>
dict[“Test”] = 10;
int n = dict[“test”];