泛型
泛型
委托让方法参数化,泛型让类型参数化。泛型就是指通过参数化类型来实现在同一份代码上操作多种数据类型的目的。
泛型类型
class C {} //非泛型
class C<T> {} //有一个泛型参数的类
struct C<U, V> {} //有两个泛型参数的结构
C<int> cTest = new C<int>(); //实例化
泛型类型的类型参数在实例化时会根据T的实际类型,自动编译成实际类型的代码。泛型特别适用于使用同一段代码处理不同的数据类型的场景。可以创建泛型类、泛型接口、泛型结构、泛型委托、泛型事件、泛型方法。
类型参数的结束
class Shop<T> where T : Customer {}
class Vendor<T, K>
where T : Customer
where K : Customer, ICustomer
{}
约束类型 | 说明 |
---|---|
where T : class | 类型实参必须是引用类型,包括任何类、接口、委托或数组类型 |
where T : struct | 类型实参必须是值类型,可以是任何值类型,但不包括Nullable |
where T : | 类型实参必须是指定的类或它的派生类 |
where T : |
类型实参必须是指定的接口或实现了该接口的类型,可以指定多个接口约束,约束接口也可以是泛型的 |
where T : new() | 类型实参必须有一个无参数的公共构造函数 |
where T : U | 类型实参T必须是类型实参U,或者是U的派生类,这称做裸类型约束 |
where子句之间没有顺序,但where子句内部有次序要求,要求如下:
- 如果有class、struct或者这三种结构豆类,则必须放在第一位
- 之后是
约束类型,数量不限 - 如果有new()约束类型,则必须放在最后
泛型类
封闭式构造类型:Stack<string>
开放式构造类型:Stack<T>
和Stack<BaseClass>
泛型类和继承相关:
- 泛型类可以从非泛型类、封装式构造类型或开放式构造类型继承
class BaseClass {}
class BaseClassGeneric<T> {}
class ClassOne<T> : BaseClass {} //继承自非泛型
class ClassTwo<T> : BaseClassGeneric<int> {} //继承自封闭式构造类型
class ClassThree<T> : BaseClassGeneric<T> {} //继承自开放式构造类型
- 非泛型类可以从封闭式构造类型继承,但无法从开放式构造类型或裸类型参数参数继承,因为在运行时客户端代码无法提供实例化基类所需的类型实参
class ClassOne : BaseClassGeneric<int> {} // OK
class ClassTwo : BaseClassGeneric<T> {} //Wrong
class ClassThree : T {}
- 开放式构造类型继承的泛型类,如果基类的某个类型参数没有在派生类中使用,则必须在基类中为这个类型参数提供类型实参
class BaseClassGeneric<T, U> {}
class ClassOne<T> : BaseClassGeneric<T, int> {} //OK
class ClassTwo<T, U> : BaseClassGeneric<T, U> {} //OK
class ClassThree<T> : BaseClassGeneric<T, U> {} //Wrong
- 从开放式构造类型继承的泛型类如果有约束,那么泛型类也必须指定约束,并且泛型类的约束是基类型约束的超集或包含基类型约束
class BaseClass<T> where T : System.IComparable<T>, new() {}
class ClassOne<T> : BaseClass<T> where T : System.IComparable<T>, new() {}
- 开放式构造类型和封闭式构造类型可以用做方法参数
void Copy<T>(List<T> list1, List<T> list2) {}
void Move(List<int> list1, List<int> list2) {}
泛型参数是不变的,如果输入参数指定List,当你试图提供List
泛型方法中,某此情况下,编译器可以根据传入的方法参数判断出类型参数的具体类型,此时就可以省略掉尖括号中的类型实参。
协变和逆变
协变:泛型参数定义的类型只能作为方法的返回类型,不能用作方法参数的类型,且该类型直接间接地继承自接口方法的返回值类型。可以使用out关键字,将泛型类型参数声明为协变参数
interface ISample<out T>
{
T Function();
}
class SampleTest<T> : ISample<T>
{
public T Function()
{
return default(T);
}
}
逆变:泛型参数定义的类型只能作为方法参数的类型,不能用作返回类型,且该类型是接口方法的参数类型的基类型,称为逆变。可以使用关键字in,将泛型类型参数声明为逆变参数
interface ISample<in T> { void Function(T arg); }
class Sample<T> : ISample<T>
{
public void Function(T arg)
{
Console.WriteLine(arg);
}
}