泛型的作用和约定
提高性能
拆箱和装箱
从值类型转换为引用类型为装箱
,把引用类型转换为值类型为拆箱
装箱和拆箱很容易使用,但是性能损失比较大,尤其是遍历许多项的时候。
List<T>
不使用对象,在使用时定义类型
var list = new List<int>();
list.Add(44); // no boxing
int item = list[0]; // mo unboxing
不妨将
List<T>
看做一种新的类型,不在特意的和C++的模板相比较;
泛型类型的命名约定
- 泛型类型的名称用
T
作为前缀; -
泛型类型允许使用任意类替代,且只使用了一个泛型类型就可以用
T
作为泛型类型的名称public class List<T>{} public class LinkedList<T>{}
-
若泛型类型有特定需求(例如必须实现一个接口或派生自基类),或者使用了
两个或多个泛型类型
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e); public delegate TOutput Converter<TInput, TOutput>(TInput from); public class SOrtedList<TKey, TValue>{}
# 泛型类的功能
## 默认值
创建泛型时,不能把null赋予泛型类型 ;在这个时候,我们就需要
default
,将null
赋予引用类型,0赋值于值类型;
public T GetDocument()
{
T doc = default(T);
lock(this)
{
doc = documentQueue.Dequeue();
}
return doc;
}
## 约束
若泛型类需要调用泛型类型中的方法,就必须添加约束
where
泛型支持以下几种约束类型:
约束 | 说明 |
---|---|
where T: struct | 对于结构约束,T必须是值类型 |
where T: class | T必须是引用类型 |
where T: IFoo | T必须实现接口IFoo |
where T: Foo | T 必须派生基类Foo |
where T: new() | 构造函数约束,T必须有一个默认构造函数 |
泛型类型也可以合并多个约束,where T: IFoo, new()
约束和MyMerge<T>
申明指定,T必须实现IFoo接口,且必须有一个默认构造函数,示例如下所示:
public class MyMerge<T> where T: IFoo, new()
{
// dosomething
}
继承
泛型类型可以实现泛型接口,也可以派生自类,当然也可以派生自泛型基类;
public class Base<T>{}
public class Derived<T>: Base<T> {} // 派生自泛型基类
当然,泛型类型派生自指定基类的类型:
public class Base<T>{}
public class Derived<T>: Base<string>{}
静态成员
泛型类的静态成员只能在类的一个实例*享
// 定一个泛型类的静态成员
public class StaticDemo<T>
{
public static int x;
}
StaticDemo<int>.x = 3; // 第一组静态字段 = 3
StaticDemo<string>.x = 4; // 第二组静态字段 = 4
Console.WriteLine(StaticDemo<int>.x); // 这里将会输出3
# 泛型接口
使用泛型可以定义接口,在接口定义的方法可以带泛型参数
协变
在.Net中,参数类型是
协变
的
例如,有Shape和Circle类,Circle派生自Shape,Display方法是为了接受Shape类型的参数
public void Display(Shape object){}
现在可以传递派生自Shape基类的任意对象,例如Circle,Rectangle
Circle c = new Circle(5);
Display(c); // 这里便是协变
抗变
方法的返回类型是
抗变
的
例如,若方法返回一个Shape,就不能把它赋予Circle,但是反过来就可以;
泛型接口的协变
若泛型类型使用
out
标注,泛型接口就是协变
的,意味着返回类型也是T
若泛型类型使用in
标注,泛型接口就是抗变
的,表明传入的参数类型只能是T