zoukankan      html  css  js  c++  java
  • C#高级编程:泛型优点和特性

     

      泛型是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 publicclass DocumentManager<T>
    2 {
    3 privatereadonly Queue<T> documentQueue=new Queue<T>();
    4
    5 publicvoid AddDocument(T doc)
    6 {
    7 lock(this)
    8 {
    9 documentQueue.Enqueue(doc);
    10 }
    11 }
    12
    13 publicbool IsDocumentAvailable
    14 {
    15 get
    16 {
    17 return documentQueue.Count>0;
    18 }
    19 }
    20 }
    21
    22  publicinterface IDocument
    23 {
    24 string Title{get;set;}
    25 string Content{get;set;}
    26 }
    27
    28  publicclass 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 publicstring Title{get;set;}
    41 publicstring Content{get;set;}
    42 }
    复制代码

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

    复制代码
    1 publicvoid 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 publicclass DocumentManager<TDocument>where TDocument:IDocument
    2 {
    3 ....
    4 }

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

    复制代码
    1 publicvoid DisplayAllDocument()
    2 {
    3 foreach(TDocument doc in documentQueue)
    4 {
    5 Console.WriteLine(doc.Title);
    6 }
    7 }
    复制代码

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

    1 staticvoid 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 publicclass MyClass<T>where t:IFoo,new()
    2 {
    3 ...
    4 }

      

      3)继承

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

    复制代码
    1 publicclass Base<T>
    2 {
    3
    4 }
    5
    6  publicclass Derived<T>:Base<T>
    7 {
    8
    9 }
    复制代码

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

    复制代码
    1 publicclass Base<T>
    2 {
    3
    4 }
    5
    6  publicclass Derived<T>:Base<string>
    7 {
    8
    9 }
    复制代码

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

    复制代码
    1 publicabstractclass Calc<T>
    2 {
    3 publicabstract T Add(T x,T y);
    4 publicabstract T Sub(T x,T y);
    5 }
    6
    7  publicclass SimpleCalc:Calc<int>
    8 {
    9 publicoverrideint Add(int x,int y)
    10 {
    11 return x+y;
    12 }
    13
    14 publicoverrideint Sub(int x,int y)
    15 {
    16 return x-y;
    17 }
    18 }
    复制代码

      4)静态成员

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

    1 publicclass StaticDemo<T>
    2 {
    3 publicstaticint x;
    4 }

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

    1 StaticDemo<string>.x=4;
    2 StaticDemo<int>.x=5;
    3 Console.WrileLine(StaticDemo<string>.x); //将会输出4
     
     
    分类: C#随笔
    4
    0
     
     
     
    « 上一篇:《C#高级编程》之事件学习笔记
    » 下一篇:C#设计模式学习笔记-单例模式
    posted on 2011-01-13 20:38 阿寻 阅读(3719) 评论(1) 编辑 收藏
  • 相关阅读:
    LeetCode-Palindrome Partitioning II
    LeetCode-Palindrome Partitioning
    LeetCode-Permutation Sequence
    LeetCode-Anagrams
    LeetCode-Text Justification
    LeetCode-Best Time to Buy and Sell Stock III
    LeetCode-Best Time to Buy and Sell Stock II
    LeetCode-Best Time to Buy and Sell Stock
    LeetCode-N-Queens II
    BZOJ 5390: [Lydsy1806月赛]糖果商店
  • 原文地址:https://www.cnblogs.com/mslalan/p/7373344.html
Copyright © 2011-2022 走看看