CLR类型设计之泛型(一)

           在讨论泛型以前,咱们先讨论一下在没有泛型的世界里,若是咱们想要建立一个独立于被包含类型的类和方法,咱们须要定义objece类型,可是使用object就要面对装箱和拆箱的操做,装箱和拆箱会很损耗性能,咱们接下来会用一个示例来讲明使用泛型和使用非泛型对值操做时的性能差距。可是若是使用泛型,也是一样的效果,不须要装箱和拆箱的同时泛型还保证了类型安全html

               言归正传,.Net自2.0之后就开始支持泛型,CLR容许建立泛型引用类型和泛型值类型,但不容许建立泛型枚举类型,此外,CLR还容许建立泛型接口和泛型委托。先来简单看一下泛型的语法。算法

               1、为何要有泛型?数组

               咱们在写一些方法时可能会方法名相同,参数类型不一样的方法,这种叫作重载。若是只是由于参数类型不一样里面作的业务逻辑都是相同的,那可能就是复制粘贴方法,改变参数类型,例如一些排序算法,int、float、double等类型的排序,参数数组存的数据类型不同,还有像根据索引找到List集合中的对象。可能这个对象是Person、Dog等对象,这样方法改变的只是参数类型,那就是能不能写一个方法,传递不一样的参数类型呢?因而有了泛型。安全

              2、什么是泛型?ide

              泛型经过参数化类型来实如今同一份代码上操做多种数据类型。例如使用泛型的类型参数T,定义一个类Stack<T>,能够用Stack<int>、Stack<string>或Stack<Person>实例化它,从而使类Stack能够处理int、string、Person类型数据。这样能够避免运行时类型转换或封箱操做的代价和风险,相似C++的模板。泛型提醒的是将具体的东西模糊化,这与后面的反射正好相反。post

             3、泛型的语法性能

                     需引用命名空间System.Collections.Generic,泛型List类后面添加了一个<T>,代表操做的是一个未指定的数据类型,在泛型声明过程当中,全部的类型参数放在间括号中(<>),经过逗号分隔。优化

             命名约定ui

  •    泛型类型的名称用字母T做为前缀
  •     若是没有特殊要求,泛型类型容许用任意类替代,且只使用了一个泛型类型就能够用字符T做为泛型类型名称,以下
    public class List<T>{}
  •   若是类型有特定的要求(例如必须实现一个接口或者派生自基类),或者使用了两个或两个以上泛型类型,就应给泛型类型使用描述性的名称,以下
    Public class SortedList<Tkey,Tvalue>{}

 

             泛型和非泛型性能对比this

             下面的示例说明,示例对比了使用泛型和不使用泛型的对比

 1         static void Main(string[] args)
 2         {
 3             Stopwatch stopwatch = new Stopwatch();
 4             stopwatch.Start(); //  开始监视代码运行时间
 5             List<long> listint = new List<long>();
 6             Add(listint);
 7             stopwatch.Stop(); //  中止监视
 8             TimeSpan timespan = stopwatch.Elapsed;
 9             double milliseconds = timespan.TotalMilliseconds;
10             Console.WriteLine("泛型用时"+milliseconds);
11 
12 
13             Stopwatch stopwatch1 = new Stopwatch();
14             stopwatch1.Start(); //  开始监视代码运行时间
15             ArrayList alistint = new ArrayList();
16             Addf(alistint);
17             stopwatch1.Stop(); //  中止监视
18             TimeSpan timespan1 = stopwatch1.Elapsed;
19             double milliseconds1 = timespan1.TotalMilliseconds;
20             Console.WriteLine("非泛型用时"+milliseconds1);
21 
22         }
23 
24         public static void Add(List<long> a)
25         {
26             long sum = 0;
27             for (long i = 0; i < 1000000; i++)
28             {
29                 a.Add(i);
30             }
31             foreach (var item in a)
32             {sum += item;}
33             Console.WriteLine("泛型集合结果"+sum);
34         }
35 
36 
37         public static void Addf(ArrayList a) {
38             long  sum = 0;
39             for (long i = 0; i < 1000000; i++)
40             {
41                 a.Add(i);
42             }
43             foreach (var item in a)
44             { sum+=Convert.ToInt32(item); }
45             Console.WriteLine("非泛型集合结果"+sum);
46 
47         }
View Code

            运行结果以下,在一百万次值类型循环下泛型要比非泛型省下50毫秒左右的时间,固然这点时间看起来不多,可是程序中每每不仅是示例这样简单的逻辑,每每包含很对更加复杂的逻辑,时间和性能上的差距就会变得很大。

            类型安全

           从下面的例子能够看出,非泛型集合能够赋值任何类型,取出时只须要拆箱操做,泛型集合则须要赋值指定类型值,不然就会报错,从类型的安全角度来看,泛型的类型都是符合要求的类型才能放进来,因此更安全,而非泛型的集合则须要辨别其中那些是符合类型的,那些是不须要的

1    List<int> list = new List<int>();//建立泛型集合
2             ArrayList arry = new ArrayList();//非泛型集合
3             list.Add(1);//能够
4             list.Add("zj");//报错
5 
6             arry.Add(1);//能够
7             arry.Add("张三");//能够
View Code

 

           泛型类型

          根据类型参数不一样的指定类型实参的状况,泛型类型能够分为:

  • 若是没有为类型参数提供类型实参,那么声明的就是一个未绑定泛型类型(unbound generic)
  • 若是指定了类型实参,该类型就称为 已构造类型(constructed type),然而已构造类型又能够是开放类型或封闭类型的
    • 包含类型参数的类型就是开放类型(open type)(全部的未绑定的泛型类型都属于开放类型的),
    • 每一个类型参数都指定了类型实参就是封闭类型(closed type)

       类型是对象的蓝图,咱们能够经过类型来实例化对象;那么对于泛型来讲,未绑定泛型类型是以构造泛型类型的蓝图,已构造泛型类型又是实际对象的蓝图。

        下图就是一个简单的例子,Dictionary<TKey, TValue>就是一个泛型类型(未绑定泛型类型,开放类型);经过制定类型参数,能够获得不一样的封闭类型;经过不一样的封闭类型有能够构造不一样的实例。 

                                         

          泛型类型和继承

            泛型类型仍然是类型,因此能从其余任何类型派生,使用泛型类型并指定类型实参时,实际上在CLR中定义新的类型对象,新的类型对象从泛型类型派生自的那个类型派生,

换句话说,List<T>是从object派生,因此List<string>,List<int>也都派生自object,相似地,因为DictionaryStringKey<TValue>派生自Dictionary<String,TValue>因此DictionaryStringKey<Guid>派生自Dictionary<String,Guid>,类型实参的指定和继承层次结构没有任何关系--理解这一点,有助于判断哪些转型是可以进行的,哪些转型是不能进行的。

         接下来看代码示例:

 1 internal  class Node{
 2         protected Node m_next;
 3         public Node(Node next) {
 4             m_next = next;
 5         }
 6     }
 7     internal sealed class TypeNode<T> : Node {
 8         public T m_data;
 9  
10         public TypeNode(T data) : this(data, null) { }
11  
12         public TypeNode(T data, Node next)
13             : base(next)
14         {
15             m_data = data;
16         }
17         public override string ToString()
18         {
19             return m_data.ToString() +
20                 ((m_next != null) ? m_next.ToString() : null);
21         }
22     }
23  
24 程序入口:
25  Node head = new TypeNode<Char>('.');
26  head = new TypeNode<DateTime>(DateTime.Now, head);
27  head = new TypeNode<String>("Today is ", head);
28  Console.WriteLine(head.ToString());
29 程序输出:
30 Today is 2012-11-23 16:33:14.
View Code

         定义一个非泛型的Node基类,在定义一个泛型TypedNode类(用Node类做为基类),这样依赖就能够建立一个链表,每一个节点均可以是一种具体的数据类型(非Object类型),同时防止装箱,有点递归的意思。

        代码爆炸

           使用泛型类型参数的一个方法在进行JIT编译时,CLR获取方法的IL,用指定的类型实参进行替换,而后建立恰当的本地代码。然而,这样作有一个缺点:CLR要为每种不一样的方法/类型组合生成本地代码。咱们将这个现象称为"代码爆炸"。它可能形成引用程序集的显著增大,从而影响性能。
 
            CLR内建了一些优化措施,能缓解代码爆炸。首先,假如为一个特定的类型实参调用了一个方法,之后再次使用相同的类型实参来调用这个方法,CLR只会为这个方法/类型组合编译一次。因此,若是一个程序集使用List<DateTime>,一个彻底不一样的程序集也使用List<DateTime>,CLR只会为List<DateTime>编译一次方法。
 
           CLR还提供了一个优化措施,它认为全部引用类型实参都是彻底相同的,因此代码可以共享。之因此能这样,是由于全部引用类型的实参或变量时间只是执行堆上的对象的指针,而对象指针所有是以相同的方式操做的。
 
          可是,假如某个类型实参是值类型,CLR就必须专门为那个值类型生成本地代码。由于值类型的大小不定。即便类型、大小相同,CLR仍然没法共享代码,可能须要用不一样的本地CPU指令操做这些值。

         结束语:

                   泛型的知识比预想的要多不少,一篇文章所有写完很长,会分为两个部分,将在二中继续研究泛型接口和泛型委托,协变和逆变泛型类型参数,泛型方法,和约束性

引用:http://www.cnblogs.com/wilber2013/p/4291435.html#_nav_0  田小计划 理解C#泛型

http://www.cnblogs.com/Ming8006/p/3789847.html#c.d 明-Ming 《CLR via C#》读书笔记 之 泛型

 

 

 http://www.cnblogs.com/liuhailiang/archive/2012/11/26/2788642.html    Lordbaby 泛型(三)泛型类型和继承

相关文章
相关标签/搜索