1. 什么是泛型
1.1 概述
泛型不是语法糖,而是由框架升级提供的功能,需要编译器支持。把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。
语法糖 :不改变其功能的情况下,通过改变代码的写法,让代码更具有可读性。
2. 为什么要使用泛型
2.1 泛型优点
普通方法示例:
普通方法中的参数类型都是固定的,不能随意修改。遇到相似的模块,只是处理数据类型不一样,只能写多个方法来处理不同的数据类型,容易造成方法冗余,不好维护。
//普通方法示例
static void Main(string[] args)
{
PrintInt(1);
PrintDoublc(3.3);
PrintString("Holle");
}
static void PrintInt(int x)
{
//...处理数据
Console.WriteLine($"参数: {x}");
}
static void PrintDoublc(double d)
{
//...处理数据
Console.WriteLine($"参数: {d}");
}
//... PrintString等等
泛型方法实例:
使用泛型可以把类型当作是参数一样传递(参数化类型), 而不再关注调用者传递的实体是什么。实现用一个方法传递不同的参数,做相同的事情。
//泛型方法实例
static void Main(string[] args)
{
Print<int>(1);
Print<double>(3.3);
Print<string>("Holle");
}
static void Print<T>(T x)
{
//...处理数据
Console.WriteLine($"参数: {x}");
}
使用泛型的优点 :
-
可以最大限度地重用代码、保护类型的安全以及提高性能;
-
降低了强制转换或装箱操作的成本或风险;
-
可以对泛型类进行约束以访问特定数据类型的方法。
3. 泛型运行原理
3.1 设计思想
泛型是延迟声明的:即定义的时候没有指定具体的参数类型,把参数类型的声明推迟到了调用的时候才指定参数类型。推迟一切可以推迟的,可以提升性能。
延迟思想在程序架构设计的时候很受欢迎。例如:前端UI的懒加载,分布式缓存队列、EF的延迟加载等等。
3.2 工作原理
控制台程序最终会编译成一个exe程序,exe被点击的时候,会经过JIT(即时编译器)的编译,最终生成二进制代码,才能被计算机执行。泛型加入到语法以后,编译器又做了升级,升级之后编译时遇到泛型,会做特殊的处理:生成占位符。再次经过JIT编译的时候,会把上面编译生成的占位符替换成真实的数据类型。
4. 泛型应用范围
4.1 泛型方法
// 泛型方法:为了一个方法满足不同的类型的需求
// 一个方法完成多实体的查询
// 一个方法完成不同的类型的数据展示
//多类型参数:不要关键字,不要类名称重复
public T Get<T, S, Model, Eleven, Null>(Eleven eleven)
{
throw new Exception();
}
4.2 泛型类
// 泛型类
public class GenericClass<T>
{
public T _T;
}
4.3泛型接口
// 泛型接口
public interface IGenericInterface<T>
{
//泛型类型的返回值
T GetT(T t);
}
4.4 泛型委托
//泛型委托
public delegate void SayHi<T>(T t);
4.5 泛型类继承父类
如果子类不是泛型的,那么继承的时候必须指定具体类型
// 使用泛型的时候必须指定具体类型,这里的具体类型是int
public class CommonClass : GenericClass<int>
{
}
如果子类也是泛型的,那么继承的时候可以不指定具体类型
//子类也是泛型的,继承的时候可以不指定具体类型
public class CommonClassChild<T> : GenericClass<T>
{
}
5. 泛型约束
5.1 为什么要有泛型约束
在泛型类型或泛型方法的定义中,类型参数是一个占位符,那么带来的一个问题是:编译器并不知道这个占位符类型T到底是什么对象,可以调用哪些方法,因此无约束的泛型方法所可以调用的方法就很少。因此就需要引入另一个话题:以约束换权利,或者说 有约束才有自由。
5.2 五种泛型约束
约束 | 说明 |
---|---|
T: 结构 | 类型参数必须是值类型 |
T: 类 | 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。 |
T:new() | 类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。 |
T:<基类名> | 类型参数必须是指定的基类或派生自指定的基类。 |
T:<接口> | 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。 |
。。。。。待续