泛型
- 是C#编程语言的一种结构
- 与IL紧密集成
- 也是CLR定义的,因此可以跨编程语言
- 可用于:
- 类
- 接口
- 方法
- 委托
- 作用: 避免编写,功能相同的方法和类,只创建一个方法或类,即可
- 作为对比,
Object
似乎也能做到这一点,但这不是类型安全的 - 用泛型保证了类型安全性,如果某个类不支持泛型类,编译器就会出现错误
- 作为对比,
后面将介绍:
通过泛型类,可以创建独立于类型的类
泛型方法,是独立于类型的方法
泛型委托,可以解耦出,方法中对集合所施加的算法
泛型方法的重载,允许在独立于类型的类里,如何为不同的类型实现不同的算法
泛型的优缺点
从以下5方面介绍优缺点:
- 性能
- 类型安全性
- 二进制代码重用
- 代码的扩展
- 命名约定
性能
以ArrayList
这个非泛型类和List<T>
这个泛型类,为例:
装箱:从值类型转换成引用类型。拆箱:装箱的值类型,转换成值类型。拆箱,要用类型强制转换运算符。
ArrayList
ArrayList
存储的每一个元素,都是对象,所以有以下拆装箱过程。
var list = new ArrayList();
list.Add(44); //装箱,把值类型转换成引用类型
int i1 = (int)list[0] //拆箱,把引用类型转换成值类型
foreach(int i2 in list)
{
Console.WriteLine(i2); //拆箱
}
拆装箱很容易,但是性能损失较大,遍历很多项时,性能损失尤其大。
List<T>
List<T>
,若希望T
为int
:
- 则JIT编译器会动态生成一个类
- 这个类的每个元素就是
int
, 而不是对象 - 因此不需要拆装箱
var list = new List<int>();
list.Add(44); //不装箱,list里存储的就是int类型
int i1 = list[0]; //不拆箱,也无需做类型转换
foreach(int i2 in list)
{
Console.WriteLine(i2);
}
类型安全
以ArrayList
这个非泛型类和List<T>
这个泛型类,为例:
ArrayList
能添加任意类型作为其元素- 而通过拆箱来使用元素的时候,需要强制类型转换
- 当转换无法实现的时候,就会报出运行时错误
- 这无法在编译时报错
List<T>
只能添加T
类型作为其元素- 如果试图添加其他无法转换为T的类型,会在编译时报错。
二进制代码重用
- 泛型类,定义一次,可用多种类型实例化
- 泛型类,在一种语言中定义,在任何其他.NET语言中使用
实例化泛型时,创建多少代码
//源码
var list1 = new List<int>()
var list2 = new List<string>()
C#编译器编译源码产生程序集,程序集里的IL代码:
- 尚且不会从
List<T>
,复制代码,来代表实例化过的泛型类 JIT编译器将IL代码,编译为本地代码: - 此时会为值类型,创建新类,例如为
List<int>
创建新类 - 不会为引用类型,创建新类,而是共享同一个本地类的所有相同的代码实现 原因如下:
实例化泛型类所需的内存:引用类型只需要4个字节的内存地址,而每个值类型对内存的要求都不同
命名规则
- 没有特殊要求,用字母T
- 有特殊要求,以字母T作为前缀,使用描述性的名称。特殊要求,例如:
- 必须实现一个接口
- 必须派生自一个基类
- 多个泛型类型
public class SortedList<TKey, TValue> { }
public delegate TOutput Converter<TInput, TOutput>(TInput from);
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);