实际类型参数代替泛型参数时,编译器会根据不同的类型实参重新生成类型,对于编译器来说,每个重新生成的封闭泛型类型都是一个不一样的类型,所以它们都有属于它自己的静态字段和静态函数。
类型约束就是用where关键字来限制某个类型实参的类型。
引用类型约束的表示形式为T:class,它确保传递的类型实参必须是引用类型。约束的类型参数和类型本身没有关系,即在定义一个泛型结构体时,泛型类型一样可 以被约束为引用类型。此时,结构体类型本身是值类型,而类型参数约束为引用类型,它可以为 任何的类、接ロ、委托或数组等,但不能指定以下这些特殊的引用类型:System.Object. System.Array 、 System.Delegate、System.MulticastDelegate 、System.ValueType、System.Enum 和System.Void。如下面的代码所定义的泛型类:
using System.IO; public class Sample<T> where T : Stream { public void Test(T stream) { stream.Close(); } }
在以上代码中,类型参数T设置了引用类型约束。whereT:stream的意思就是告诉编译器:传入的类型实参必须是System.IO.Stream,或者是从Stream类。如果一个类型参数没有指定约束,则默认T为System.Object类型。
值类型约束的表示形式为T:struct,它确保传递的类型实参是值类型(包括枚举),但这里的值类型不包括可空类型
public class Sample<T> where T : struct { public static T Test() { return new T(); } }
在上面的代码中,newT()是可以通过编译的,因为T是一个值类型,而所有值类型都有一个公共的无参构造函数。但如果不对T进行约束,或约束为引用类型,则上面的代码就会报错,因 为有的引用类型是没有公共的无參构造函数的。
构造函数类型约束的表示形式为T:new(),如果类型参数有多个约束,则此约束必须最后指定。构造函数类型约束确保指定的类型实参有一个公共无参构造函数的非抽象类型。这适用于所有值类型,所有非静态、非抽象、没有显式声明构造函数的类,以及显式声明了一个公共无參构造函数的所有非抽象类。
这里需要注意,如果同时指定构造器约束和struct约束,C#编译器会认为这是一个错误。因为这样的指定是多余的,所有值类型都隐式地提供了一个无参公共构造函数。就如同定义接口时指定访问类型为public一样,编译器也会报错,因为接ロー定是public的,编译器认为这样做是多余的。
T:基类名确保指定的类型实參必须是基类或派生自基类的子类;
T:接ロ名确保指定的类型实参必须是接ロ或实现了该接ロ的类;
T:U则确保T提供的类型实参必须是U提供的类型实参或派生于U提供的类型实参,即前面一个类型实参必须是后面的类型实参或后面类型实参的子类。
Class Sample<T> where T: Stream Sample<Stream> √ Sample<string> × Class Sample<T> where T: IDisposable Sample<Stream> √ Sample<StringBuilder> × Class Sample<T,U> where T: U Sample<Stream, IDispsable> √ Sample<string, IDisposable> ×
组合约束是将多个不同种类的约束合并在一起的情况。这里需要注意,没有任何一种类型既是引用类型,又是值类型,所以引用约束和值约束不能同时使用。如果存在多个转换类型约束, 且其中一个是类,则类必须放在接口的前面。不同的类型参数可以有不同的约束,但每种类型參数必须分别使用一个单独的where关键字。
有效的:
class Sample
class Sample<T,U> where T:class where U: struct。
无效的:
class Sample
class Sample
class Sample
class Sample
class Sample<T,U> where T: struct where U:class, T ( 类型形参T具有struct约束,因此T不能用作U的约束, 所以无效);
class Sample<T,U> where T:Stream, U:IDisposable (不同的类型参数可以有不同的约束,但它们分别要有一个 单独的where关键字,所以无效)。
1、协变性:指的是泛型类型参数可以从一个派生类隐式地转化为基类。使用out关键字来标记泛型参数,进而支持协变性。
2、逆变性:指的是泛型类型参数可以从一个基类隐式地转化为派生类,使用in关键字来标记泛型参数,进而支持逆变性。
3、注意事项:
并不是所有类型都支持泛型类型参数的协变和逆变性。