17.5 类型参数的约束
有时候,希望对类型实参添加一定的限制条件,以限制某个泛型类型的使用范围,让它只能用于某个特定场景。例如,我们可以规定类型实参只能是某个类型,或者某个类的派生类,等等。在本节中,我们将要学习如何为类型参数增加约束条件。要实现这一点并不难,接下来,将会通过一段例子代码进行具体说明:
❑首先定义一个类Customer,它有两个属性,分别是Name和CreditCardNo;
❑然后定义一个泛型类型Shop<T>,我们规定,这里的T只能为Customer或者它的派生类。
具体代码如代码清单17-7所示。
代码清单17-7 在泛型类中规定类型参数的具体类型
1 class Customer
2{
3 public string Name{get;set;}
4 public string CreditCardNo{get;set;}
5}
6
7 class Shop<T>
8{
9 public void Print(T customer)
10{
11 var cust=(Customer)customer;
12 System.Console.WriteLine(“姓名:{0}”,cust.Name);
13 System.Console.WriteLine(“信用卡号:{0}”,cust.CreditCardNo);
14}
15}
那么,既然T为Customer类或者其派生类,因此,就有了代码清单17-7中第11~13行的代码,首先将T类型的变量customer强制转换为Customer类型,然后访问它的两个属性:Name和CreditCardNo。从理论上讲,貌似没问题,但却无法通过编译,错误位于第11行,错误信息是:
无法将类型"T"转换为"ProgrammingCSharp4.Customer"
这说明一个问题:在为类型参数添加约束之前,类型参数将只能访问System.Object类型中的公有方法,如Equals()、GetHashCode()、ToString()、GetType()这四个方法。要想访问具体某个类型或接口的方法,如Customer类的成员,则必须对类型参数添加约束。
C#泛型只支持显式的约束,但显式的约束并非是必须的。
17.5.1 where子句
约束是通过使用上下文关键字where应用的。语法很简单,只需要在泛型类型声明的尖括号后面,使用where关键字,后跟类型参数和约束类型,中间使用冒号分隔即可。
下面使用约束来改造代码清单17-7,从而消除程序中的错误。改造的方法就是为类型参数T添加必须为Customer类或其派生类的约束,如图17-5所示。
图 17-5 使用类型参数约束后的Shop<T>泛型类
此后,在编译期间,编译器会确认我们每次传给它的类型实参是Customer类或它的派生类,并且,我们也无须再显式地将customer变量转换为Customer类型。
可以对同一个类型参数应用多个约束,也可以给多个类型参数应用约束。每个类型参数的约束都有独立的where子句。在下面的例子中,类型参数T有一个基类名约束,类型参数K有一个基类名约束和一个接口名约束,如图17-6所示。
图 17-6 类型参数约束的更多应用方式