VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > c#编程 >
  • C#泛型学习笔记

   本笔记摘抄自:https://www.cnblogs.com/dotnet261010/p/9034594.html,记录一下学习过程以备后续查用。

    一、什么是泛型

    泛型是C#2.0推出的新语法,不是语法糖,而是2.0由框架升级提供的功能。泛型类就类似于一个模板,可以在需要时为这个模板传入任何我们需要的类型。

    二、为什么使用泛型

    下面代码演示输出几种类型的相关信息:

 View Code

    运行结果如下:

    上面3个方法很相似,除了参数类型不同外,实现的功能是一样的,可以稍作优化。

    下面代码演示使用继承的方式输出几种类型的相关信息:

 View Code

    功能实现没有问题,只是object与其它类型的转换,涉及到装箱和拆箱的过程,这个是会损耗程序的性能的。

    三、泛型类型参数

    在泛型类型或方法的定义中,泛型类型参数可认为是特定类型的占位符。

    下面代码演示使用泛型的方式输出几种类型的相关信息:

 View Code

    运行结果如下:

    1、为什么泛型可以解决上面的问题呢?

    泛型是延迟声明的:即定义的时候没有指定具体的参数类型,把参数类型的声明推迟到调用的时候才给它指定。 

    2、泛型究竟是如何工作的呢?

    程序执行原理:控制台程序最终会编译成一个exe程序。当exe被点击的时候,会经过JIT(即时编译器)的编译,最终生成二进制代码才能被计算机执行。

    泛型工作原理:泛型加入到语法以后,VS自带的编译器做了升级,升级之后编译时若遇到泛型,会做特殊的处理:生成占位符。然后经过JIT编译的时候,

会把上面编译生成的占位符替换成具体的数据类型。

    下面代码演示泛型占位符:

 View Code

    运行结果如下:

    3、泛型性能问题

    下面代码演示泛型性能测试:

 View Code

    运行结果如下:

    从结果可以看出,泛型的性能是最高的。

    四、泛型类

    下面代码演示泛型类:

 View Code

    运行结果如下:

    五、泛型接口

    注:泛型在声明的时候可以不指定具体的类型,继承的时候也可以不指定具体类型,但是在使用的时候必须指定具体类型。

    下面代码演示泛型接口:

 View Code

    运行结果如下:

    六、泛型委托

    下面代码演示泛型委托:

 View Code

    运行结果如下:

    七、泛型约束

    泛型约束,实际上就是约束的类型T,使T必须遵循一定的规则。比如T必须继承自某个类或者T必须实现某个接口等等。

    怎样给泛型指定约束?其实也很简单,只需要where关键字,加上约束的条件。

    泛型约束总共有五种:

约束 s说明
T:结构 类型参数必须是值类型
T:类 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。
T:new() 类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。

    7.1基类约束

    下面代码演示基类约束:

 View Code

    运行结果如下:

    注:基类约束时,基类不能是密封类,即不能是sealed类。sealed类表示该类不能被继承,在这里用作约束就无任何意义了,因为sealed类没有子类。

    7.2接口约束

    下面代码演示接口约束:

 View Code

    运行结果如下:

    7.3引用类型约束 class

    引用类型约束保证T一定是引用类型的。

    下面代码演示引用类型约束:

 View Code

    运行结果如下:

    7.4值类型约束 struct

    值类型约束保证T一定是值类型的。

    下面代码演示值类型约束:

 View Code

    运行结果如下:

    7.5无参数构造函数约束 new() 

    下面代码演示无参数构造函数约束:

 View Code

    运行结果如下:

    从上面可以看出,泛型约束可以有多个,但是有多个泛型约束时,new()约束要放到最后。

    八:泛型的协变和逆变

    协变和逆变是在.NET 4.0的时候出现的,只能放在接口或者委托的泛型参数前面,out协变covariant,用来修饰返回值;in:逆变contravariant,用来修饰

传入参数。

    下面代码演示父类与子类的声明方式:

 View Code

    以上代码是可以正常运行的。假如使用下面的声明方式,是否正确呢?

List<Animal> list = new List<Cat>();

    答案是错误的,因为List<Animal>和List<Cat>之间没有父子关系。

    解决方法是使用协变的方式:

IEnumerable<Animal> List1 = new List<Animal>();
IEnumerable<Animal> List2 = new List<Cat>();

    按F12查看IEnumerable定义:

    可以看到,在泛型接口的T前面有一个out关键字修饰,而且T只能是返回值类型,不能作为参数类型,这就是协变。使用协变以后,左边声明的是基类,

右边的声明可以是基类或者基类的子类。

    协变除了可以用在接口上面外,还可以用在委托上面:

Func<Animal> func = new Func<Cat>(() => null);

    除了使用.NET框架定义好协变以外,我们也可以自定义协变:

//使用自定义协变
ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>();

    再来看看逆变

    在泛型接口的T前面有一个In关键字修饰,而且T只能方法参数,不能作为返回值类型,这就是逆变。

 View Code

    使用自定义逆变:

//使用自定义逆变
ICustomerListIn<Cat> customerListCat1 = new CustomerListIn<Cat>();
ICustomerListIn<Cat> customerListCat2 = new CustomerListIn<Animal>();

    协变和逆变也可以同时使用。

    下面代码演示自定义协变与逆变:

 View Code

    运行结果如下:

    九、泛型缓存

    类中的静态类型无论实例化多少次,在内存中只会有一个,静态构造函数只会执行一次。在泛型类中,T类型不同,每个不同的T类型,都会产生一个不同

的副本,所以会产生不同的静态属性、不同的静态构造函数。

    下面代码演示泛型缓存:

 View Code

    运行结果如下:

    从上面的截图中可以看出,泛型会为不同的类型都创建一个副本,因此静态构造函数会执行5次,另外每次静态属性的值都是一样的。利用泛型的这一特性,可以实现缓存。

    注:只能为不同的类型缓存一次;泛型缓存比字典缓存效率高;泛型缓存不能主动释放。


相关教程