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 ...
随机推荐
- unity3D学习序幕
目前,我所在的公司不适合我长久发展,在一好友的提示下,我决定以unity3D程序员的身份,返回我2013年工作过的那家公司.关于unity3D,除了几年前一点模糊的记忆,其他都是一篇空白.今年年初我买 ...
- web页面在微信里打开,字体颜色不正常显示
问题:写的web项目在微信里的webview里打开(iphone手机),会出现颜色的不识别.写的是白色,数字的部分会过了3-5秒后,变成黑色! 原因:在iphone手机里,数字的部分(具体的长度没有测 ...
- Atitti 图像处理 特征提取的科技树 attilax总结
Atitti 图像处理 特征提取的科技树 attilax总结 理论 数学,信号处理,图像,计算机视觉 图像处理 滤波 图像处理 颜色转换 图像处理 压缩编码 图像处理 增强 图像处理 去模糊 图像处理 ...
- 妙味H5交互篇备忘
document.addEventListener( 'touchstart', function(e){ e.preventDefault(); } ); 在document上增加touchst ...
- 某种数列问题 (一场欢乐赛的T2)
个人觉得挺难的一道DP题 不会 没有思路 于是去找的正解 于是.. #include <iostream> #include <cstring> #define Max 100 ...
- js中substr,substring,indexOf,lastIndexOf等的用法
1.substrsubstr(start,length)表示从start位置开始,截取length长度的字符串. var src="images/off_1.png";alert( ...
- JS函数调用
function SayHello(word) { console.log(word); } function execute(Somefunction,value) { Somefunc ...
- 定位(position)
position :属性规定元素的定位类型 语法: position : static | absolute | fixed | relative JavaScript语法:object.style. ...
- assert的基本用法
assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制,如C,C++和Eiffel等,但是支持的形式不尽相同,有的是通过语言本身.有的是通过库函数等.另外,从理论上来 ...
- Linq to Sql 左连接查询
var query = from t0 in context.ExpressSendMaster join t1 in context.Supplier on t0.SupplierCode equa ...