C# 深入了解泛型
本文是根据网上&书本总结来的。
1. 介绍
泛型程序设计是程序设计语言的一种风格或范式。 泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时(instantiate)作为参数指明这些类型。
.NET Framework泛型的参数只可以代表类,不能代表个别对象。由于.NET Framework泛型的类型参数之实际类型在运行时均不会被消除,运行速度会因为类型转换的次数减少而加快。另外,使用GetType()方法可于程序运行时得知泛型及其类型参数的实际类型,更可以运用反射编程。
2. 为什么需要泛型
任何API只要将object作为参数类型或返回类型使用,就可能在某个时候涉及强制类型转换。设计只有一个类,并将object作为根的层次结构,将使一切变得更加简单。但是,object类型本身是极其‘愚钝’的一个存在。要用一个object做真正有意义的事情,几乎都要对它进行强制类型转换。
泛型能对性能有增强的作用,首先编译器能执行更多的检查,所以执行时的检查可以少做。其次,JIT能够聪明地处理值类型,能消除很多情况下的装箱和拆箱处理。某些情况下,无论在速度上还是在内存消耗上,有泛型和没有泛型的结果会大相径庭。
泛型带来的好处非常像静态语言较之动态语言的优点:更好的编译时检查,更多在代码中能直接表现的信息,更多的IDE支持,更好的性能。原因很简单,使用一个不能区分不同类型的常规API(比如ArrayList),相当于在一个动态环境中访问那个API。顺便说一下,反过来说通常不成立:动态语言在许多情况下都具备大量的优势,但这些情况很少适用于选择泛型和非泛型的API。当你能合理地使用泛型时,通常会毫不犹豫地选择泛型。
3. 我们通过例子来学习
1)我们用泛型来创建一个创建List的方法:
public List<T> MakeList<T>(T a, T b)
{
return new List<T>() {a, b};
}
List<string> myList = MakeList("str1", "str2");
从上面的例子可以看出,只要我们传入自己的类型,就能创造返回自己的类型了。
2)现在我们深入一点。
就像实例字段从属于一个实例一样,静态字段从属于声明他们的类型。如果在SomeClass中声明了静态字段x,不管创建SomeClass的多少个实例,也不管从SomeClass派生出多少个类型,都只有一个SomeClass.x字段。
每个封闭类型都有它自己的静态字段集。我们为不同的封闭类型设置字段的值,然后打印这些值,证明它们是各自独立的。
public class TypeWithField<T>
{
public static string field; public static void PrintField()
{
Console.WriteLine(field +": " + typeof(T).Name);
}
}
下面我们打印它们的值:
TypeWithField<int>.field = "f1";
TypeWithField<string>.field = "s2";
TypeWithField<DateTime>.field = "t3"; TypeWithField<int>.PrintField();
TypeWithField<string>.PrintField();
TypeWithField<DateTime>.PrintField();
可以看到有这些值:

所以,基本规则是:每个封闭类型又一个静态字段。同样的规则也适用于静态初始化程序和静态构造函数。然而,一个泛型类型可能嵌套在另一个泛型类型中,而且一个类型可能有多个泛型参数。虽然听起来很复杂,但它的工作方式与你想象的差不多。
4. JIT编译器如何处理泛型
对于所有不同的封闭类型,JIT的职责就是将泛型的IL转换成本地代码,使其能真正运行起来。从某些方面来说,我们并不需要知道具体的转换过程是怎么样的。只需要留意内存和CPU时间即可。如果JIT为每个封闭类型都单独生成本地代码,就像这些类型相互之间没有任何联系一样,我们将不会感觉出太大差异的。但是JIT的作者十分聪明,非常有必要看看他们做了什么。
首先看一个简单的、只有一个类型参数的情况。为方便讨论,我们使用List<T>作为例子。JIT为每个以值类型作为类型实参的封闭类型都创建不同的代码。然而,所有使用引用类型(string、Stream、StringBuilder等)作为类型实参的封闭类型都共享相同的本地代码。之所以能这样做,是由于所有引用都具有相同的大小(32位CLR上是4字节,64位CLR上是8字节。但是,在任何一个特定的CLR中,所有引用都具有相同的大小)。无论实际引用的是什么,引用(构成的)数组的大小是不会发生变化的。栈上引用所需的空间始终是相同的。无论使用的类型是什么,都可以使用相同的寄存器优化措施,即使是List<Reason>也不例外。
如上面所述,每个类型还可以有它自己的静态字段,但可执行代码本身是可以重用的。当然,JIT采用的仍然是‘懒人’原则。除非需要,否则不会为List<int>生成代码。而一旦生成代码,代码就会缓存起来,以备将来再次使用List<int>。
理论上,至少对一些值类型来说,代码是可以共享的。但JIT必须十分谨慎,不仅要考虑到大小,还要考虑到垃圾回收的问题,JIT必须能快速识别一个struct值中的引用是否是活着的。然而假如值类型具有相同的大小,而且就GC看来具有相同的‘内存需求量’,那么是应该能够共享代码的。
可以关注本人的公众号,多年经验的原创文章共享给大家。

C# 深入了解泛型的更多相关文章
- 一起学 Java(三) 集合框架、数据结构、泛型
一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...
- .NET面试题系列[8] - 泛型
“可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用.“ - Jon Skeet .NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] ...
- C#4.0泛型的协变,逆变深入剖析
C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...
- 编写高质量代码:改善Java程序的151个建议(第7章:泛型和反射___建议106~109)
建议106:动态代理可以使代理模式更加灵活 Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发.我们知道一个静态代理是通过主题角色(Prox ...
- 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...
- C#泛型详解(转)
初步理解泛型: http://www.cnblogs.com/wilber2013/p/4291435.html 泛型中的类型约束和类型推断 http://www.cnblogs.com/wilber ...
- C# 泛型
C# 泛型 1.定义泛型类 在类定义中包含尖括号语法,即可创建泛型类: class MyGenericClass<T> { //Add code } 其中T可以遵循C#命名规则的任意字符. ...
- java8中lambda表达式的应用,以及一些泛型相关
语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...
- java 泛型
1.Student stu =tool.getObj();右边得到的是Object类型,需要向下转型,强转换. 2. 3. 4.泛型方法不能被静态修饰这样写 5.如果想定义定义静态泛型方法,只能这样写 ...
- Java泛型的历史
为什么Java泛型会有当前的缺陷? 之前的章节里已经说明了Java泛型擦除会导致的问题,C++和C#的泛型都是在运行时存在的,难道Java天然不支持“真正的泛型”吗? 事实上,在Java1.5在200 ...
随机推荐
- 学习OpenCV,看这些!
OpenCV简介: OpenCV 是一款功能强大的跨平台计算机视觉开源库,可以用于解决人机交互.物体检测.人脸识别等领域的问题.库本身是采用 C++ 编写的,但是同时也对 Python, Java, ...
- 【CodeVS】1293
输入输出样例 思路:看到题目我萌第一眼想到的肯定是求联通快对吧,但是这个联通快有点奇特,因为 这样他也算是一个联通快.解决此题其实有三种解法:1)宽搜(这个符合基本法):2)并查集:3)灌水法 但是蒟 ...
- 深入理解Hadoop集群和网络
导读:云计算和Hadoop中网络是讨论得相对比较少的领域.本文原文由Dell企业技术专家Brad Hedlund撰写,他曾在思科工作多年,专长是数据中心.云网络等.文章素材基于作者自己的研究.实验和C ...
- php 语言特点
PS:绝大多数用php的企业/ 项目 活不到雇佣得起月薪35k以上的php程序员那一天,也是php码农在10年经验的时候普遍不如java程序员的原因之一. PS2: 由于薪资提升太快,很多php码农跳 ...
- JS正则表达式之特殊符号
在正则表达式中,许多标点符号具有特殊含义,比较难记,现归纳备个份: 这些符号有:^ $ . * + - ? = ! : | \ / ( ) [ ] { } 1."[ ]"表示字符 ...
- 在代理中托管特殊方法的python代码实现
任务简单的介绍是: 在新风格对象模型中,Python操作其实是在类中查找特殊方法的(经典对象是在实例中进行操作的),现在需要将一些新风格的实例包装到代理中,,此代理可以选择将一些特殊的方法委托给内部的 ...
- 3.使用secureCRT连接PC,LINUX,开发板
1.设置secureCRT(可选项):http://www.linuxyw.com/linux/gongxiang/20130505/161.html 2.使用secureCRT远程登录linux 3 ...
- VMware虚拟机服务的vmware-hostd自动启动和停止
安装了虚拟机 任务管理器会出现vmware-hostd.exe 占用了80端口,导致xampp打不开,所以就想关闭vmware,解决方案如下: 开始——运行——services.msc,找到VM打头 ...
- Chrome 开发者工具的使用
Console 那里是可以调节上下文的,不同的文档上下文互相隔离,默认是top,也就是当前打开的页面.这个功能在页面包含 iframe 或者开发插件的时候才用得到. 早期版本的 Resource 已经 ...
- JS---控制键盘事件
键盘事件汇总: 1.onkeydown 键盘按下时触发; 2.onkeyup 键盘按下后抬起触发的事件 3.onkeypress 这个事件在用户按下并放开任何字母数字键时发生(不常用) keyCo ...