泛型中的静态字段和静态函数
实际类型参数代替泛型参数时,编译器会根据不同的类型实参重新生成类型,对于编译器来说,每个重新生成的封闭泛型类型都是一个不一样的类型,所以它们都有属于它自己的静态字段和静态函数。
为什么要用泛型
- 封装泛型方法实现代码的重用
- 避免装箱和拆箱导致性能的损失
类型参数的约束
类型约束就是用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 where T:class, IDisposable, new();
class Sample<T,U> where T:class where U: struct。
无效的:
class Sample where T: class, struct (没有任何类型既是引用类型又是值类型,所以无效);
class Sample where T: Stream, class(引用类型约束应该为第一个约束,放在最前面,所以无效);
class Sample where T: new(), Stream (构造函数约束必須放在最后面,所以无效);
class Sample where T: IDisposable, Stream (类必须放在接口前面,所以无效);
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、注意事项:
并不是所有类型都支持泛型类型参数的协变和逆变性。
- 只有接口和委托才支持协变和逆变(如Func、Action),类或泛型方法的类型参数都不支持协变和逆变;
- 协变和逆变只适用于引用类型,值类型不支持协变和逆变(因为可变性存在引用转换的过程,而值类型变量存储的就是对象本身,并不是对象的引用),所以し151无法转化为 Ienumerable
- 必须显式地用in或out来标记类型参数。
- 委托的可变性不要在多播委托中使用。
Func stringfunc =()=>"";