17.2 泛型类型
在方案三中使用了本章要介绍的泛型类型,它可以十分优雅地解决上述方案所遇到的这类问题。泛型类型较普通类型多了一个类型参数,例如:<T>。其中T是一个通用的类型占位符,实际上除了字母T,类型参数也可以根据需要使用其他的标识符(例如V、U、TPerson、Person、User等)。
如下是非泛型类型和泛型类型的示例:
class C{}//非泛型类
class C<V>{}//泛型类,具有一个泛型参数V
struct C<U,V>{}//泛型结构,具有两个泛型参数U和V
泛型类型的类型参数,将在实例化时根据T的实际类型,CLR会自动编译为本地代码,运行效率和代码质量都有一定提高,并且保证了数据的类型安全。下面使用泛型来改写代码清单17-1,也就是方案三的内容,如代码清单17-3所示。
代码清单17-3 使用泛型重写栈的例子
1 class Stack<T>
2{
3 private int_size;
4 private int_pointer;
5 private T[]_db;
6
7 public Stack(int size)
8{
9_size=size;
10_db=new T[_size];
11}
12
13 public void Push(T data)
14{
15 int tmp=_pointer++;
16 if(tmp<_size)
17{
18_db[tmp]=data;
19}
20}
21
22 public T Pop()
23{
24 return_db[—_pointer];
25}
26}
相比非泛型Stack类版本,上述代码有如下几点不同:
❑类的声明部分变成了Stack<T>;
❑_db数组成了T[]类型;
❑Push方法的参数成了T类型;
❑Pop方法的返回类型成了T类型。
图17-2说明了泛型类型相对于非泛型类型带来的灵活性增加,原来需要针对每种类型都有一个专门的类,现在只需要一个泛型类型就足够,代码也得到了更大程度的复用。
图 17-2 泛型类型相较于非泛型类型更大程度复用了代码
可见,使用泛型是将类的共同行为提取出来,而将变化的部分抽象为类型参数。换句话说,泛型特别适用于使用同一段代码处理不同的数据类型的场景。
在C#中,除了可以创建泛型类以外,还可以创建自己的泛型接口、泛型结构、泛型方法、泛型事件和泛型委托,其中除了方法是成员以外,其余均为类。