泛型是CLR和编程语言提供的一种特殊机制,它用于满足“算法重用” 。可以想象一下一个只有操作的参数的数据类型不同的策略模式,完全可以用泛型来化为一个函数。以下是它的优势:类型安全给泛型算法应用一个具体的数据类型时,如果不兼容这种类型, ...
泛型是CLR和编程语言提供的一种特殊机制,它用于满足“算法重用” 。
可以想象一下一个只有操作的参数的数据类型不同的策略模式,完全可以用泛型来化为一个函数。
以下是它的优势:
- 类型安全
- 给泛型算法应用一个具体的数据类型时,如果不兼容这种类型,就会编译错误或者报异常。
- 更清晰的代码
- 更佳的性能
- 用泛型可以有效避免装箱拆箱的操作,且无需在进行强制转换时验证是否类型安全,这两点都有效提高了代码的性能。
这就是为什么List<T>淘汰了ArrayList的原因,特别是在进行值类型操作时,因为装箱拆箱过多而差距很大。
约定:泛型参数要么为T要么以大写T开头,例如List<T>。
FCL中的泛型
System.Collections.Generic和System.Collections.ObjectModel命名空间中提供了多个泛型集合类和接口。
System.Collections.Concurrent命名空间则提供get='_blank'>线程安全的泛型集合类。
System.Array类则提供了大量的静态泛型方法。
泛型的基础结构
.net 2.0才有泛型。
- 开放类型和封闭类型
- 之前我们讲到CLR会为各种类型创建类型对象,同样一个新的泛型类TroyList<T>也会创建一个类型对象,我们将具有泛型参数的类型称为开放类型。
- 而指定了泛型类型实参的泛型类型称为封闭类型,例如:TroyList<int>。
- 可以构造封闭类型的实例
- 如果TroyList<T>定义了静态字段或者方法,那么TroyList<int>和TroyList<string>之间并不共享,因为这其实是两个不同的类型对象。
- 泛型类型的继承
- 使用泛型类型并指定类型实参后,实际上是一个新的封闭类型,新的类型对象从泛型类型派生自的那个类型派生。即List<T>派生自Object,那么List<int>就派生自Object。
- 关于代码**的优化
- 看到这里你可能想到了,一个开放类型实际上会有多个封闭类型,比如一个List<T>会有List<int>,List<string>等N多封闭类型。实际上就是N多的类型对象,生成N多的重复代码,于是这被称作代码**。
- 关于优化:
- 两个不同的程序集用到同一种封闭类型,只会由JIT编译器变异一次
- CLR认为所有用引用类型做类型实参的封闭类型完全相同,所以代码可以共享。也就是说List<String>和List<Stream>的方法编译后的代码可以通用。因为操作的不同的引用类型的地址大小都是一样的。
委托和接口的逆变和协变泛型类型实参
泛型委托和接口的每个泛型类型参数都可标记为协变量和逆变量,利用此功能可实现相同类型但实参类型不同的委托和接口的相互转换。(很绕,不明白可以看下面)
- 不变量
- 逆变量
- 意味着泛型类型参数可以从一个类更改为它的派生类。用in标记,逆变量泛型类型参数只能出现在输入位置。
- 协变量
- 意味着泛型类型参数可以从一个类更改为它的基类。用out标记,协变量泛型类型参数只能出现在输出位置。
举个例子
public class 基类 { } public class 派生类 : 基类 { } public class Test{ public delegate TResult MyFunc<in T1, out TResult, T2>(T1 a, T2 b);//第一个为逆变量,第二个为协变量,第三个为不变量 void show() { MyFunc<基类, 基类, 基类> fn1 = null; //以下注释为我自己的理解方式,只是为了方便理解而已 MyFunc<派生类, 基类, 基类> fn2 = fn1;//MyFunc<派生类, 派生类, 基类> fn2 = fn1;转换错误 MyFunc<基类, Object, 基类> fn3 = fn1;//MyFunc<Object, Object, 基类> fn3 = fn1;转换错误 MyFunc<派生类, Object, 基类> fn4 = fn1; } }
原标题:【C#进阶系列】11 泛型
关键词:C#
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:
admin#shaoqun.com
(#换成@)。