一、什么是泛型
泛型,即“参数化类型”
我们来看以下代码,目的很明确,就是显示参数类型,这种类似的代码或者说只有参数类型不同,但是功能相同时,我们如何让代码写的更优雅?
在泛型没有出现的.Net FrameWork 1.0时代,可以这样来写:
我们来看这样子写的,确实更优雅了许多,但是这样子写会有两个问题,至于哪两个问题,看完全文你将得到答案
为了代码更加优雅,在.Net FrameWork 2.0时代,出现了泛型
二、泛型的种类
2.1 泛型类
2.2 泛型接口
2.3 泛型方法
2.4 泛型委托
三、泛型的继承
3.1 基类和接口
3.2 普通类
3.3 泛型类
四、泛型的约束
4.1 定义三个类使用
4.2 不使用泛型
可以看到 前两个正常打印,第三个抛异常,这就是object什么都可以传,导致的一个问题:传参不安全,很明显Japanese没有相对应的属性
4.3 使用泛型
可以看到这里我们对 输入进行的约束必须为People或其子类
所以,当我们再传Japanese时,直接提示不过
4.4 约束的种类
- 引用类型约束
(T : class), 即T必须是一个引用类型。必须放在第一个。 - 值类型约束
(T : struct) 。 必须放在第一个。 - 无参构造函数约束
new() , 必须放在最后一个。 - 转换类型约束(基类约束、接口约束)
class Sample where T : Stream
class Sample<T1, T2> where T1: T2 (如Sample<Stream, IDisposable>
class Sample where T : Stream, IEnumerable, IComparable 可以多个接口,但是只能一个class.
指定的类不能是结构,密封类比如string,或者这些特殊类型:
System.Object
System.Enum
System.ValueType
System.Delegate
五、泛型的性能
5.1 测试代码
定义三个方法
分别是直接类型、Object、泛型
每个方法循环1亿次,判定性能
5.2 测试结果
我们可以很清楚的看出
直接类型≈泛型<Object
原因很明显
使用Object 涉及装箱和拆箱的过程
而泛型是延迟声明,使用的时候直接类型替换占位符,所以基本和直接类型性能一致
六、协变逆变
6.1 错误示范
6.2 协变
为了解决上述的问题,增加了协变的概念 ,在声明时增加out关键字,只能作为返回值使用
用它就可以这样写
IEnumerable<Bird> birds3 = new List<Bird>();
IEnumerable<Bird> birds4 = new List<Sparrow>();
委托可以这样写
Func<Bird> func = new Func<Bird>(() => null);
Func<Bird> func2 = new Func<Sparrow>(() => null);
6.3 逆变
逆变 :协变的逆过程,只能作为传参使用 关键字in
协变的逆过程
ICustomerIn<Sparrow> customerIn = new CustomerIn<Sparrow>();
ICustomerIn<Sparrow> customerIn2 = new CustomerIn<Bird>();
Action<Sparrow> action = new Action<Sparrow>((Sparrow i) => { });
Action<Sparrow> action2 = new Action<Bird>((Bird i) => { });
七、泛型的缓存
不同的类型在调用泛型时
- 每一个类型都会创建一份副本
- 每一个类型只会创建一次