本文是根据网上&书本总结来的。

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# 深入了解泛型的更多相关文章

  1. 一起学 Java(三) 集合框架、数据结构、泛型

    一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...

  2. .NET面试题系列[8] - 泛型

    “可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用.“ - Jon Skeet .NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] ...

  3. C#4.0泛型的协变,逆变深入剖析

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

  4. 编写高质量代码:改善Java程序的151个建议(第7章:泛型和反射___建议106~109)

    建议106:动态代理可以使代理模式更加灵活 Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发.我们知道一个静态代理是通过主题角色(Prox ...

  5. 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...

  6. C#泛型详解(转)

    初步理解泛型: http://www.cnblogs.com/wilber2013/p/4291435.html 泛型中的类型约束和类型推断 http://www.cnblogs.com/wilber ...

  7. C# 泛型

    C# 泛型 1.定义泛型类 在类定义中包含尖括号语法,即可创建泛型类: class MyGenericClass<T> { //Add code } 其中T可以遵循C#命名规则的任意字符. ...

  8. java8中lambda表达式的应用,以及一些泛型相关

    语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...

  9. java 泛型

    1.Student stu =tool.getObj();右边得到的是Object类型,需要向下转型,强转换. 2. 3. 4.泛型方法不能被静态修饰这样写 5.如果想定义定义静态泛型方法,只能这样写 ...

  10. Java泛型的历史

    为什么Java泛型会有当前的缺陷? 之前的章节里已经说明了Java泛型擦除会导致的问题,C++和C#的泛型都是在运行时存在的,难道Java天然不支持“真正的泛型”吗? 事实上,在Java1.5在200 ...

随机推荐

  1. ajax/fetch上传富文本时出现中文乱码的解决方案(百分号问题)

    最近正在编写自己的项目,其中遇到了nodejs后台接受到的富文本参数显示中文乱码的问题 一开始我以为是字符编码方式的错误,于是在请求参数的地方设置了utf-8,也就是: headers: { 'Con ...

  2. Java Web学习笔记--JSP for循环

    JSP for循环 <%@ page language="java" contentType="text/html; charset=UTF-8" %&g ...

  3. Redmine管理项目1-自定义属性

    先是点击页面导航条(最上面那排菜单,有主页.我的工作台.项目.管理.帮助等)上的“管理”菜单,看到下图: 看到里面的“自定义属性”菜单了吧,点击它,可以看到下面的界面: 点击那个“新建自定义属性”菜单 ...

  4. shell 字符串包含

    转自:Shell判断字符串包含关系的几种方法 现在每次分析网站日志的时候都需要判断百度蜘蛛是不是真实的蜘蛛,nslookup之后需要判断结果中是否包含"baidu"字符串 以下给出 ...

  5. 负载均衡,最理想使用 redis实现session共享

    负载均衡在多台php服务器负载均衡的情况下,第一秒请求是a服务器,第二秒请求是b服务器, session必须放在一个公共的服务器,最理想是使用 redis实现session共享.内存的速度比磁盘访问快 ...

  6. asp.net如何把一个tostring类型转化为dateTime类型

    Convert.ToDateTime(dr["consult_DealTime"].ToString()).ToString("yyyy-MM-dd"); co ...

  7. CoreJavaE10V1P3.5 第3章 Java的基本编程结构-3.5 操作符

    最基本的操作为赋值操作,= 即赋值操作符 基本的算术操作为加.减.乘.除取模.除取余数,其对应操作符为 +.-.*./.% 算术操作与赋值操作联合衍生为:+=:-=:*=:/=:%=: 由于处理器硬件 ...

  8. Front-End(二)——HTML

    本文主要对html迭代学习中的要点.冷点简述罗列. html之前也说过,主要为了描述页面的结构和内容,合理使用结构化的标签,<h1>.<div>等,有利于前端开发,也有利于搜索 ...

  9. phonegap 随笔

    开发者论坛 http://bbs.phonegapcn.com/forum.php phone调用android本地方法 http://blog.csdn.net/crazyman2010/artic ...

  10. 安卓平台 全面支持软解和硬解的SDK-Demo源代码开放

    专业做视频编解码的SDK开发工作. 2015年12月1日10:46:55: 更新到1.5.0版本 功能列表: 基本播放: 1,正常播放, 支持MP4,FLV,AVI,TS,3GP,RMVB,WM,WM ...