原文出处:http://www.cnblogs.com/xun126/archive/2011/01/13/1933838.html

泛型是CLR 2.0的一个新特性,在CLR 1.0中,要创建一个灵活的类或方法,但该类或方法在编译期间不知道使用什么类,就得以Object类为基础。而Object在编译期间没有类型安全性,因此必须进行强制类型转换,同时,给值类型使用Object类会有性能损失。泛型类使用泛型类型,并可以根据需要用特定的类型替换泛型类型。这就保证了类型安全性:如果某个类型不支持泛型类,编译器就会报错。

  一、泛型有以下几个优点:

  1)性能

  对值类型使用非泛型集合类,在把值类型转换为引用类型,和把引用类型转换为值类型时,需要进行装箱和拆箱操作。装箱和拆箱的操作很容易实现,但是性能损失较大。假如使用泛型,就可以避免装箱和拆箱操作。

1 ArrayList list=new ArrayList(); 2 list.Add(20); //装箱,list存放的是object类型元素,须将值类型转化为引用类型 3 int i=(int)list[0]; //拆箱,list[0]的类型是object,要赋值就得把引用类型转化为值类型

  如果换成泛型编程,就不会有装箱和拆箱的性能损失。

1 List<T> list=new List<int>(); 2 list.Add(20); //因为指定了用int来实例化,因此不必装箱 3 int i=list[0]; //同样地,访问时也不需要拆箱

  

  2)类型安全

  与ArrayList类一样,如果使用对象,可以在这个集合中添加任意类型。

  如果使用非泛型编程,如下代码,就有可能在某些情况下会发生异常。

1 ArrayList list=new ArrayList(); 2 list.Add(20); 3 list.Add("string"); 4 list.Add(new MyClass()); 5 6  foreach(int i in list) 7 { 8 Console.WriteLine(i); //这里会有个异常,因为并不是集合中的所有元素都可以转化为int 9  }

  如果该用泛型编程,则可以避免这种异常,让编译器检查出错误。

1 List<int> list=new List<int>(); 2 list.Add(20); 3 lsit.Add("string"); //编译时报错,只能报整数类型添加到集合中 4 list.Add(new MyClass()); //同上

  

  3)二进制代码重用

  泛型可以定义一次,用许多不同的类型实例化,不需要像C++模板那样访问源代码。泛型可以在一种语言中定义,在另一种.NET语言中使用。

  4)代码的扩展

  因为泛型类的定义会放在程序集中,所以用某个类型实例化泛型泛型类不会在IL代码中复制这些类。但是,在JIT编译器把泛型类编译为内部代码时,会给每个值类型创建一个新类。引用类型共享同一个内部类的所有实现代码。这是因为引用类型在实例化的泛型类中只需要4字节的内存单元(32位系统),就可以引用一个引用类型。值类型包含在实例化的泛型类的内存中。而每个值类型对内存的要求都不同,所以要为每个值类型实例化一个新类。

  二、泛型类的特性

  1)默认值

在给类型T初始化时,要注意不能把null赋予泛型类型。因为泛型类型也可以实例化为值类型,而null只能用于引用类型。为了解决这个问题,可以用default关键字。通过default关键字,将null赋予引用类型,将0赋予值类型。


1 public T GetDoucumet() 2 { 3  T doc=default(T); 4  lock(this) 5  { 6  doc=documentQueue.Dequeue(); 7  } 8  return doc; 9 }

补充:default关键字根据上下文可以有多种含义。switch语句使用default定义默认情况。在泛型中,根据泛型类型是引用类型还是值类型,default关键字用于将泛型类型初始化为null或0。

  2)约束

  如果泛型类需要调用泛型类型上的方法,就必须添加约束。


 1 public class DocumentManager<T>  2 {  3  private readonly Queue<T> documentQueue=new Queue<T>();  4   5  public void AddDocument(T doc)  6  {  7  lock(this)  8  {  9  documentQueue.Enqueue(doc); 10  } 11  } 12  13  public bool IsDocumentAvailable 14  { 15  get 16  { 17  return documentQueue.Count>0; 18  } 19  } 20 } 21  22  public interface IDocument 23 { 24  string Title{get;set;} 25  string Content{get;set;} 26 } 27  28  public class Document:IDocument 29 { 30  public Document() 31  { 32  } 33  34  public Document(string title,string content) 35  { 36  this.title=title; 37  this.content=content; 38  } 39  40  public string Title{get;set;} 41  public string Content{get;set;} 42 }

  如果使用DocumentManager<T>类显示文档,可以将类型T强制转换为IDocument接口


1 public void DisplayAllDocument() 2 { 3  foreach(T doc in documentQueue) 4  { 5  Console.WriteLine(((IDocument)doc).Title); 6  } 7 }

  假如类型T没有执行IDocument接口,这个类型转换就会生成一个异常,因此需给DocumentManager<T>类定义一个约束:T必须执行IDocument接口,为了在泛型类型的名称中指定该要求,将T改为TDocument。wherer子句指定了执行IDocument接口的要求。

1 public class DocumentManager<TDocument>where TDocument:IDocument 2 { 3  .... 4 }

  这样编写foreach语句就可以让类型T包含Title属性。


1 public void DisplayAllDocument() 2 { 3  foreach(TDocument doc in documentQueue) 4  { 5  Console.WriteLine(doc.Title); 6  } 7 }

  在Main()方法中,DocumentManager<T>类用Document类型来实例化,而Document类型执行了需要的IDocument接口。

1 static void Main() 2 { 3  DocumentManager<Document> dm=new DocumentManager<Document>(); 4  dm.AddDocument(new Document("Title A","A")); 5  dm.AddDocument(new Document("Title B","B")); 6  dm.DisplayAllDocument();

  除此之外,泛型还有几种约束类型。如下:

  1)where T:struct  使用结构约束。类型T必须是值类型

  2)where T:class  类约束指定,类型T必须是引用类型

  3)where T:IFoo  指定类型T必须执行接口IFoo

  4)where T:Foo  指定类型T必须派生于基类Foo

  5)where T:new()  构造函数约束,指定类型T必须有一个默认构造函数

  6)where T:U  类型T1派生于泛型类型T2。该约束也成为裸类型约束。

  注意:使用泛型类型还可以合并多个约束。where T:IFoo,new()约束和MyClass<T>声明指定,类型T必须执行IFoo接口,且必须有一个默认构造函数。

1 public class MyClass<T>where t:IFoo,new() 2 { 3  ... 4 }

  

  3)继承

   泛型类型可以执行泛型接口,也可以派生于一个类。泛型类可以派生于泛型基类:


1 public class Base<T> 2 { 3  4 } 5  6  public class Derived<T>:Base<T> 7 { 8  9 }

  要求必须重复接口的泛型类型,或者必须指定基类的类型。


1 public class Base<T> 2 { 3  4 } 5  6  public class Derived<T>:Base<string> 7 { 8  9 }

  所以,派生类可以是泛型类或非泛型类。如可以定义一个抽象的泛型基类,它在派生类中用一个具体的类型实现。


 1 public abstract class Calc<T>  2 {  3  public abstract T Add(T x,T y);  4  public abstract T Sub(T x,T y);  5 }  6   7  public class SimpleCalc:Calc<int>  8 {  9  public override int Add(int x,int y) 10  { 11  return x+y; 12  } 13  14  public override int Sub(int x,int y) 15  { 16  return x-y; 17  } 18 }

  4)静态成员

  泛型类的静态成员需要特别关注。泛型类的静态成员只能在类的一个实例中共享。

1 public class StaticDemo<T> 2 { 3  public static int x; 4 }

  对一个string类型和一个int类型使用了StaticDemo<T>类,所以存在两组静态字段:

1 StaticDemo<string>.x=4; 2 StaticDemo<int>.x=5; 3 Console.WrileLine(StaticDemo<string>.x); //将会输出4

《C#高级编程(第六版)》泛型学习笔记(一):泛型优点和特性 (转载)的更多相关文章

  1. C#高级编程 (第六版) 学习 第七章:委托和事件

    第七章 委托和事件 回调(callback)函数是Windows编程的一个重要方面,实际上是方法调用的指针,也称为函数指针. .Net以委托的形式实现了函数指针的概念,.Net的委托是类型安全的. 委 ...

  2. C#高级编程 (第六版) 学习 第六章:运算符和类型强制转换

    第六章 运算符和类型强制转换 1,运算符 类别 运算符 算术运算符 + - * / % 逻辑运算符 & | ^ ~ && || ! 字符串连接运算符 + 增量和减量运算符 ++ ...

  3. C#高级编程 (第六版) 学习 第一章:.Net体系结构

    第一章 .Net体系结构 1,公共语言运行库(Common Language Runtime, CLR) .Net Framework的核心是其运行库的执行环境,称为公共语言运行库,或.Net运行库. ...

  4. C#高级编程(第六版)学习:第三十一章:Windows窗体

    第三十一章 Windows窗体 创建Windows窗体应用程序 在文本编辑器中输入: /* * form.cs * a simple windows form * */ using System; u ...

  5. C#高级编程 (第六版) 学习 第五章:数组

    第五章 数组 1,简单数组 声明:int[] myArray; 初始化:myArray = new int[4]; 为数组分配内存. 还可以用如下的方法: int[] myArray = new in ...

  6. C#高级编程 (第六版) 学习 第四章:继承

    第四章 继承 1,继承的类型 实现继承: 一个类派生于一个基类型,拥有该基类型所有成员字段和函数. 接口继承 一个类型只继承了函数的签名,没有继承任何实现代码.   2,实现继承 class MyDe ...

  7. C#高级编程 (第六版) 学习 第三章:对象和类型

    第三章 对象和类型 1,类和结构 类存储在托管堆上 结构存储在堆栈上   2,类成员 类中的数据和函数称为类成员 数据成员 数据成员包括了字段.常量和事件   函数成员 方法:与某个类相关的函数,可以 ...

  8. C#高级编程 (第六版) 学习 第二章:C#基础

    第二章 基础 1,helloworld示例: helloworld.cs using System; using System.Collections.Generic; using System.Li ...

  9. 《UNIX环境高级编程 第2版》读书笔记

    CH1-2:基础知识.标准化 1 文件和目录 文件名:不能含/(分隔路径)和null(终止路径),255字符. 目录处理:opendir() readdir() closedir() 更改工作目录:c ...

  10. 《JavaScript高级程序设计 第3版》-学习笔记-3

    P84-P137页, 这一章看的真久,这个月事太多了.有些内容在代码注释里没提出来了 1.JS强大的数组类型,元素类型任意,提供了非常多的操作数组的方法和属性 /* 数组类型 */ //stack v ...

随机推荐

  1. 【swift学习笔记】三.使用xib自定义UITableViewCell

    使用xib自定义tableviewCell看一下效果图 1.自定义列 新建一个xib文件 carTblCell,拖放一个UITableViewCell,再拖放一个图片和一个文本框到tableviewc ...

  2. c#资料

    类型系统: 运行模型: 整型: 浮点: 财务: 布尔: 字符: 引用类型: Console控制字符: {序号,空间:控制字符与精度} 如:{0,3:C2} {空间:#.00} 其中,#表示该位置如果有 ...

  3. HTTP原型

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...

  4. 随堂软工团队小测——git协同

    No Bug 031402401鲍亮 031402402曹鑫杰 031402403常松 031402412林淋 031402418汪培侨 031402426许秋鑫 功能模块划分 方法 功能 main( ...

  5. 软件工程(FZU2015)赛季得分榜,第二回合

    目录 第一回合 第二回合 第三回合 第四回合 第五回合 第6回合 第7回合 第8回合 第9回合 第10回合 第11回合 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分: 团队项目分 ...

  6. [转]Ext ComboBox 默认选中某一项

    原文地址:http://blog.csdn.net/liuguxing/article/details/8623190 项目中经常用到选择框,需要从后台异步加载数据,可单独写一个组件进行加载 App. ...

  7. 【BZOJ 1087】【SCOI 2005】互不侵犯King

    http://www.lydsy.com/JudgeOnline/problem.php?id=1087 很简单的状压,需要预处理,我两个状态可不可以挨着的预处理出错WA了好几次. 这个位运算预处理好 ...

  8. PathGradientBrush类进行渐变颜色的填充

    private void Form1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; GraphicsPath gp ...

  9. <<< 如何查看自己是外网还是内网

    判断的方法很简单,就是看你的网络中有没有路由器,不管是有线路由还是无线路由,只要你的网络中用了路由,那你就是内网,用路由器的网络有一个特点,那就是只要路由器在开着,那你开了电脑之后就可以直接上网,不需 ...

  10. Day2-python基础2

    本次学习内容 1.for循环 2.continue and break 3.while循环 4.运算符 5.数据类型 数字 字符串 列表 1.for循环 猜年龄的游戏完善下,只有三次机会 for i ...