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 类型参数的约束 - 图1

图 17-5 使用类型参数约束后的Shop<T>泛型类

此后,在编译期间,编译器会确认我们每次传给它的类型实参是Customer类或它的派生类,并且,我们也无须再显式地将customer变量转换为Customer类型。

可以对同一个类型参数应用多个约束,也可以给多个类型参数应用约束。每个类型参数的约束都有独立的where子句。在下面的例子中,类型参数T有一个基类名约束,类型参数K有一个基类名约束和一个接口名约束,如图17-6所示。

17.5 类型参数的约束 - 图2

图 17-6 类型参数约束的更多应用方式