泛型
? 对于泛型的解释我们先看下百科的解释:泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。
? 泛型是 .Net Framework 2.0提出的,我对他的理解就是 泛型的出现就是为了解决用一个方法,满足不同的参数类型,做相同的事的需求。
1、没有泛型的年代
我们只能看类型写方法,尽管他们的功能是一样的参数是不同类型的,那时候我们没有办法,例如:
? 当然,我们承认一点就是说,ShowString() 和ShowInt()这两个方法绝对是最快的,因为它就是自己类型的操作一点多余的损耗也没有, 但是没用啊 !! 你看着这么多重复的代码你不烦吗?
我们是这样重复代码的,那时候能够解决不同参数的办法只有一个,就是用基类 Object
2、泛型的出现
泛型的出现上面说了是.Net Framework 2.0出现的,他的作用就是为了解决相同功能不同参数的需求。
先说名一下,代码中的 T 不仅仅可以是任意东西,中文应该也可以,主要不是系统关键字都可。
我们对比一下我们上面写的ShowObject方法,这两者有什么区别呢?
区别就在于 Object是所有类型的父类,它可以转换成任何类型,但是在转换的时候Objcet 会有装箱和拆箱两个过程,这样会多一倍的损耗。
我们可能疑问了,为什么Object 会有装箱拆箱 而泛型却没有?
这个就要说到了泛型的机制了,泛型的运行方法是 推迟声明,推迟一切能推迟的东西。泛型没有写死参数类型,推迟到调用的时候才去指定参数类型
3、泛型的调用
很简单吧,用一对尖括号里面指定参数类型,后面的括号里面指定实参就行,只要注意好一点就可以,尖括号里面的别和后面小括号的类型一致
当然要说的就是 没有尖括号也行,因为我们说了泛型是延迟声明,系统会 也不应该是说系统 应该是说你的编译器会帮你自动推算这个类型应该是什么。
4、泛型是如何工作的呢?
? 控制台程序最终会编译成一个exe程序,exe被点击的时候,会经过JIT(即时编译器)的编译,最终会生成二进制代码,才能被计算机执行,泛型加入到语法以后,VS自带的编译器又做了升级,升级之后编译时遇到泛型,会做特殊的处理:生成占位符。再次经过JIT编译的时候,会把上面编译成的占位符替换成具体的数据类型。
请看下面一个例子:
Console.WriteLine(typeof(List<>));
Console.WriteLine(typeof(Dictionary<,>));
结果:
? 从上面的截图中可以看出:泛型在编译之后会生成占位符。占位符以后如果有时间的话,可以仔细研究下。
注意:占位符需要在英文输入法状态下才能输入,只需要按一次波浪线(数字1左边的键位)的键位即可,不需要按Shift键。
5、 泛型类
? 泛型类封装了不针对任何特定数据类型的操作。泛型类常用于容器类,如链表、哈希表、栈、队列、树等等。这些类中的操作,如对容器添加、删除元素,不论所存储的数据是何种类型,都执行几乎同样的操作。
6、泛型接口
? 不论是为泛型容器类,还是表示容器中元素的泛型类,定义接口是很有用的。把泛型接口与泛型类结合使用是更好的用法,比如用IComparable
7、泛型委托
8、普通类可以继承泛型类
在C#中申明的普通类也可以继承泛型类,但是我没有遇到过这样的需求,感觉碰到的可能性不是很大。注意泛型在声明的时候可以不指定具体的类型,但是在使用的时候必须指定具体类型
如果子类也是泛型的,那么继承的时候可以不指定具体类型。
9、泛型约束
? 要检查表中的一个元素,以确定它是否合法或是否可以与其他元素相比较,那么编译器必须保证:客户代码中可能出现的所有类型参数,都要支持所需调用的操作或方法。这种保证是通过在泛型类的定义中,应用一个或多个约束而得到的。一个约束类型是一种基类约束,它通知编译器,只有这个类型的对象或从这个类型派生的对象,可被用作类型参数。一旦编译器得到这样的保证,它就允许在泛型类中调用这个类型的方法。上下文关键字where用以实现约束。同一个类型参数可应用多个约束。约束自身也可以是泛型类。并且泛型的约束可以是多个且可以多种类型。
class MyList<T> where T: Employee, IEmployee, IComparable<T>, new() {…}
下面是五种约束
约束 | 描述 |
---|---|
where T: struct | 类型参数必须为值类型。 |
where T : class | 类型参数必须为引用类型。 |
where T : new() | 类型参数必须有一个公有、无参的构造函数。当于其它约束联合使用时,new()约束必须放在最后。 |
where T : | 类型参数必须是指定的基类型或是派生自指定的基类型。 |
where T : | 类型参数必须是指定的接口或是指定接口的实现。可以指定多个接口约束。接口约束也可以是泛型的。 |