“约束”这个词可能会引起歧义,有些人可能认为对泛型参数设定约束是限制参数的使用,实际情况正好相反。没有约束的泛型参数作用很有限,倒是“约束”让泛型参数具有了更多的行为和属性。
查看下面的代码,我们会发现参数t1或参数t2仅仅具有object的属性和行为,所以几乎不能在方法中对它们做任何的操作:
class SalaryComputer
{
public int Compare<T>(T t1,T t2)
{
return 0;
}
}
class Salary
{
public int BaseSalary{get;set;}
public int Bonus{get;set;}
}
但是,在加了约束之后,我们会发现参数t1和t2变成了一个有用的对象。由于为其指定了对应的类型,t1和t2现在就是一个Salary了,在方法的内部,它拥有了属性BaseSalary和Bonus,代码如下所示。
class SalaryComputer
{
public int Compare<T>(T t1,T t2)where T:Salary
{
if(t1.BaseSalary>t2.BaseSalary)
{
return 1;
}
else if(t1.BaseSalary==t2.BaseSalary)
{
return 0;
}
else
{
return-1;
}
}
}
那么可以为泛型参数指定哪些约束呢?如下所示:
1)指定参数是值类型(除Nullable外),可以有如下形式:
public void Method1<T>(T t)where T:struct
{
}
2)指定参数是引用类型,可以有如下形式:
public void Method1<T>(T t)where T:class
{
}
public void Method2<T>(T t)where T:Salary
{
}
注意,object不能用来作为约束。
3)指定参数具有无参数的公共构造方法,可以有如下形式:
public void Method1<T>(T t)where T:new()
{
}
注意,CLR目前只支持无参构造方法约束。
4)指定参数必须是指定的基类,或者派生自指定的基类。
5)指定参数必须是指定的接口,或者实现指定的接口。
6)指定T提供的类型参数必须是为U提供的参数,或者派生自为U提供的参数。如下形式:
class Sample<U>
{
public void Method1<T>(T t)where T:U
{
}
}
7)可以对同一类型的参数应用多个约束,并且约束自身可以是泛型类型。
在编码过程中,应该始终考虑为泛型参数设定约束。正像本建议开始的时候所说,约束使泛型参数成为一个实实在在的“对象”,让它具有了我们想要的行为和属性,而不仅仅是一个object。