C#中的泛型

 

转载:C#遭之泛型

摘自:http://birdshover.cnblogs.com/articles/392127.html

泛型(generic)是C#语言2.0跟通用语言运行时(CLR)的一个初特征。泛型为.NET框架引入了花色参数(type
parameters)的概念。类型参数使得设计类和方式时,不必确定一个还是多个实际参数,其的现实参数可延缓到客户代码中声明、实现。这意味使用泛型的门类参数T,写一个类MyList<T>,客户代码可以如此调用:MyList<int>,
MyList<string>或
MyList<MyClass>。这避免了运转时类型转换或装箱操作的代价和高风险。

 

 

 

 

目录

C# 遭遇之泛型. 1

如出一辙、泛型概述. 2

次、泛型的长处. 5

其三、泛型类型参数. 7

季、类型参数的律. 8

五、泛型类. 11

六、泛型接口. 13

七、泛型方法. 19

八、泛型委托. 21

九、泛型代码中的default 关键字. 23

十、C++ 模板和C# 泛型的区分. 24

十一 、运行时面临的泛型. 25

十二 、基础类库中之泛型. 27

 

 

 

同等、泛型概述

   
泛型类和泛型方法兼复用性、类型安全以及大效率为一身,是和的相应的非泛型的接近和方所不及。泛型广泛用于容器(collections)和针对性容器操作的方式中。.NET框架2.0底类库提供一个新的命名空间System.Collections.Generic,其中涵盖了部分初的基于泛型的容器类。要物色新的泛型容器类(collection
classes)的演示代码,请参见基础类库中之泛型。当然,你也可创建和谐的泛型类和方,以供您自己之泛化的方案和设计模式,这是种类安全还迅速之。下面的言传身教代码以一个略的泛型链表类作为示范。(多数景象下,推荐用由.NET框架类库提供的List<T>类,而休是创造自己的表。)类型参数T于差不多地处使,具体品种一般在这些地方来指明表中元素的路。类型参数T有以下几栽用法:

l        在AddHead方法被,作为艺术参数的类别。

l        在公措施GetNext中,以及嵌套类Node的
Data属性中当返回值的门类。

l        在嵌套类中,作为个人成员data的品种。

 

留意一点,T对嵌套的类Node也是有效的。当用一个具体类来落实MyList<T>时——如MyList<int>——每个出现了之T都使就此int代替。

 

using System;

using System.Collections.Generic;

 

public class MyList<T> //type parameter T in angle brackets

    {

        private Node head;

// The nested type is also generic on T.

        private class Node          

        {

            private Node next;

//T as private member data type:

            private T data;         

//T used in non-generic constructor:

            public Node(T t)        

            {

                next = null;

                data = t;

            }

            public Node Next

            {

                get { return next; }

                set { next = value; }

            }

//T as return type of property:

            public T Data           

            {

                get { return data; }

                set { data = value; }

            }

        }

        public MyList()

        {

            head = null;

        }

//T as method parameter type:

        public void AddHead(T t)    

        {

            Node n = new Node(t);

            n.Next = head;

            head = n;

        }

 

        public IEnumerator<T> GetEnumerator()

        {

            Node current = head;

 

            while (current != null)

            {

                yield return current.Data;

                current = current.Next;

            }

        }

    }

 

脚的演示代码演示了客户代码如何利用泛型类MyList<T>,来创造一个整数阐明。通过简单地改成参数的色,很容易改写下面的代码,以创造字符串或外自定义类型的申。

 

class Program

    {

        static void Main(string[] args)

        {

//int is the type argument.

           MyList<int> list = new MyList<int>();

            for (int x = 0; x < 10; x++)

                list.AddHead(x);

 

            foreach (int i in list)

            {

                Console.WriteLine(i);

            }

            Console.WriteLine(“Done”);

        }

    }

 

第二、泛型的长处

本着初版本的通用语言运行时与C#语言的局限,泛型提供了一个解决方案。以前种的泛化(generalization)是据种及全局基类System.Object的交互转换来促成。.NET框架基础类库的ArrayList容器类,就是这种局限的一个例。ArrayList是一个老大便利的容器类,使用中管需改变就好储存任何引用类型或者值类型。

 

//The .NET Framework 1.1 way of creating a list

ArrayList list1 = new ArrayList(); 

list1.Add(3);

list1.Add(105);

//…

ArrayList list2 = new ArrayList();

list2.Add(“It is raining in Redmond.”);

list2.Add(“It is snowing in the mountains.”);

//…

 

但是这种便利是生代价的,这得将另外一个参加ArrayList的援类型或者值类型都隐式地朝达更换成System.Object。如果这些要素是值类型,那么当进入到列表中时时,它们要吃装箱;当又收复它们经常,要拆箱。类型转换和装箱、拆箱的操作都下降了性能;在必迭代(iterate)大容器的情景下,装箱和拆箱的震慑或者大眼看。

 

旁一个受制是短编译时之品种检查,当一个ArrayList把其余项目且更换为Object,就无法以编译时防备客户代码类似这样的操作:

 

ArrayList list = new ArrayList(); 

//Okay.  

list.Add(3); 

//Okay, but did you really want to do this?

list.Add(.“It is raining in Redmond.”);

 

int t = 0;

//This causes an InvalidCastException to be returned.

    foreach(int x in list)

{

  t += x;

}

 

虽说这么了合法,并且有时是故意这样创建一个含有不同品类元素的器皿,但是拿string和int变量放在一个ArrayList中,几乎是于打错误,而此似是而非直到运行的时段才会为察觉。

 

在1.0版和1.1版的C#言语中,你只有通过编制好之一定项目容器,才会免免.NET框架类库的器皿类中泛化代码(generalized
code)的生死存亡。当然,因为这么的近乎无法为另外的数据类型复用,也就失泛型的独到之处,你得为每个需要仓储的花色又写该类。

 

ArrayList和任何一般之好像真正需要的凡同种途径,能叫客户代码在实例化之前指定所用的一定数据类型。这样尽管不需要进步类型转换为Object,而且编译器可以同时开展路检查。换句话说,ArrayList需要一个品种参数。这正是泛型所提供的。在System.Collections.Generic命名空间中的泛型List<T>容器里,同样是将元素加入容器的操作,类似这样:

The .NET Framework 2.0 way of creating a list

List<int> list1 = new List<int>();

//No boxing, no casting:

list1.Add(3);

//Compile-time error:

list1.Add(“It is raining in Redmond.”);

 

以及ArrayList相比,在客户代码中唯一增加的List<T>语法是宣称和实例化中的品类参数。代码略微复杂的报恩是,你创造的表明不仅比ArrayList更安全,而且肯定地进一步快,尤其当表中的因素是值类型的时节。

 

其三、泛型类型参数

   

   
在泛型类型或者泛型方法的概念着,类型参数是一个占位符(placeholder),通常也一个要命写字母,如T。在客户代码声明、实例化该品种的变量时,把T替换为客户代码所指定的数据类型。泛型类,如泛型概述中为起底MyList<T>类,不能够因此作as-is,原因在她不是一个着实的类,而重新如是一个类的蓝图。要运用MyList<T>,客户代码必须于尖括号内指定一个路参数,来声称并实例化一个曾构造类型(constructed
type)。这个特定类的类别参数可以是编译器识别的别样类型。可以创建任意数量之曾构造类型实例,每个使用不同的门类参数,如下:

 

MyList<MyClass> list1  = new MyList<MyClass>();

MyList<float> list2 = new MyList<float>();

MyList<SomeStruct> list3 = new MyList<SomeStruct>();

 

   
在这些MyList<T>的实例中,类中出现的每个T都将在运行的早晚让种参数所代表。依靠这样的交替,我们唯有用定义类的代码,就创造了三单独立的种类安全还迅速之对象。有关CLR执行替换的详细信息,请参见运行时遭到之泛型。

 

季、类型参数的封锁

 

如若使反省表中的一个因素,以确定它是不是合法要是否足以同任何因素相较,那么编译器必须管:客户代码中恐出现的保有类型参数,都如支持所要调用的操作还是艺术。这种保险是由此以泛型类的概念着,应用一个或者多单约束若赢得的。一个封锁类型是同等栽基类约束,它打招呼编译器,只有这个路的目标要于夫类别派生的靶子,可为当作类型参数。一旦编译器得到这么的管教,它就同意以泛型类中调用这个路的道。上下文关键字where用以实现约束。下面的以身作则代码说明了利用基类约束,为MyList<T>类增加效益。

 

public class Employee

{

 public class Employee

    {

        private string name;

        private int id;

        public Employee(string s, int i)

        {

            name = s;

            id = i;

        }

 

        public string Name

        {

            get { return name; }

            set { name = value; }

        }

        public int ID

        {

            get { return id; }

            set { id = value; }

        }

 

    }

}

class MyList<T> where T: Employee

{

 //Rest of class as before.

  public T FindFirstOccurrence(string s)

  {

   T t = null;

   Reset();

   while (HasItems())

   {

      if (current != null)

      {

//The constraint enables this:

         if (current.Data.Name == s)

         {

            t = current.Data;

            break;

         }

         else

         {

            current = current.Next;

         }

      } //end if

   } // end while

  return t;

  }

}

 

封锁使得泛型类能够使Employee.Name属性,因为兼具为类型T的元素,都是一个Employee对象可能一个连续自Employee的靶子。

 

与一个类型参数可下多独约束。约束自己也可是泛型类,如下:

 

class MyList<T> where T: Employee, IEmployee, 
IComparable<T>,  new()

{…}

 

    下表列出了五近乎约:

 

约束
描述
where T: struct
类型参数必须为值类型。
where T : class
类型参数必须为类型。
where T : new()
类型参数必须有一个公有、无参的构造函数。当于其它约束联合使用时,new()约束必须放在最后。
where T : <base class name>
类型参数必须是指定的基类型或是派生自指定的基类型。
where T : <interface name>
类型参数必须是指定的接口或是指定接口的实现。可以指定多个接口约束。接口约束也可以是泛型的。

 

 

品种参数的格,增加了而是调用的操作及方的数据。这些操作以及法吃拘束类型及其派生层次中的花色的支撑。因此,设计泛型类或艺术时,如果对泛型成员实施外赋值以外的操作,或者是调用System.Object中所未曾底主意,就待以品种参数达到利用约束。

 

尽制类型参数的一般用法

从未约束的品类参数,如公有类MyClass<T>{…}中的T,
被称为最制类型参数(unbounded type
parameters)。无限制类型参数有以下规则:

l        不可知用运算符 != 和 ==
,因为无法确保具体的类参数能够支持这些运算符。

l        它们得以和System.Object相互转换,也可是显式地更换成为外接口类型。

l       
可以同null比较。如果一个无比制类型参数和null比较,当此类型参数为值类型时,比较的结果总为false。

 

 

无类型约束

当自律是一个泛型类型参数时,它就叫无类型约束(Naked type
constraints)。当一个发出路参数成员方法,要管其的参数约束为夫所在类的档次参数时,无路约束非常有因此。如下例所示:

 

class List<T>

{

      //…

    void Add<U>(List<U> items) where U:T {…}

}

 

以点的演示中,
Add方法的前后文中的T,就是一个无类型约束;而List类的上下文中的T,则是一个最好制类型参数。

 

无类型约束为可据此当泛型类的定义着。注意,无项目约束一定也如跟外品类参数一起以尖括号受宣示:

//naked type constraint

public class MyClass<T,U,V> where T : V

 

盖编译器只认为无类型约束是由System.Object继承而来,所以富含无类型约束之泛型类的用充分点儿。当你要强制两个档次参数有继续关系时,可针对泛型类以无类型约束。

 

五、泛型类

 

 

泛型类包装了不针对任何特定数据类型的操作。泛型类常用于容器类,如链表、哈希表、栈、队列、树等等。这些看似中之操作,如针对容器添加、删除元素,不论所蕴藏的数目是何种类型,都履行几乎相同的操作。

 

本着大部分状况,推荐使用.NET框架2.0看似库中所提供的容器类。有关以这些类似的详细信息,请参见基础类库中的泛型。

 

常见,从一个曾经部分具体类来创造泛型类,并每次将一个类别变更吧项目参数,直至达到日常和可用性的极品平衡。当创建而协调的泛型类时,需要着重考虑的事项发生:

l       
哪些路应泛化为品种参数。一般的法则是,用参数表示的种越多,代码的油滑和复用性也不怕越充分。过多之泛化会促成代码难以为另外的开发人员理解。

l       
如果出约,那么类型参数需要什么约束。一个出色的习惯是,尽可能使用最深之束缚,同时保证可以拍卖所有需要处理的花色。例如,如果你懂得你的泛型类才打算采用引用类型,那么就算利用之看似的约束。这样好防止误中采取值类型,同时可对T使用as运算符,并且检查空引用。

l       
把泛型行为放在基类中尚是子类中。泛型类可以开基类。同样非泛型类的宏图着也承诺考虑当下一点。泛型基类的持续规则    

l       
是否实现一个要多单泛型接口。例如,要统筹一个于依据泛型的容器中开创元素的切近,可能用贯彻类似IComparable<T>的接口,其中T是此类的参数。

 

泛型概述中产生一个略泛型类的例证。

 

品种参数和约束之条条框框对泛型类的行事(behavior)有部分黑的影响,——尤其是对此继续与成员只是访问性。在征是问题面前,理解一些术语十分重点。对于一个泛型类Node<T>,客户代码既可由此点名一个档次参数来创造一个查封构造类型(Node<int>),也得以保留项目参数不指定,例如指定一个泛型基类来创造开放构造类型(Node<T>)。泛型类可以持续自具体类、封闭构造类型或放构造类型:

 

// concrete type

class Node<T> : BaseNode

//closed constructed type

class Node<T> : BaseNode<int>

//open constructed type

class Node<T> : BaseNode<T>

 

非泛型的具体类可延续自封闭构造基类,但未可知持续自开结构基类。这是因客户代码无法提供基类所急需的档次参数。

 

//No error.

class Node : BaseNode<int>

//Generates an error.

class Node : BaseNode<T>

 

泛型的现实类可继承自开放构造类型。除了与子类共用的类型参数外,必须为具备的门类参数指定项目,如下代码所示:

//Generates an error.

class Node<T> : BaseNode<T, U> {…}

//Okay.

class Node<T> : BaseNode<T, int>{…}

 

延续自开放结构类型的泛型类,必须指定:

Generic classes that inherit from open constructed types must specify
must specify constraints that are a superset of, or imply, the
constraints on the base type:

 

class NodeItem<T> where T : IComparable<T>, new() {…}

class MyNodeItem<T> : NodeItem<T> where T :
IComparable<T> , new(){…}

 

 

泛型类型可以使多种类型参数与束缚,如下:

class KeyType<K,V>{…}

class SuperKeyType<K,V,U> where U : IComparable<U>, where V
: new(){…}

 

绽放结构以及查封构造类型型可作为方法的参数:

void Swap<T>(List<T> list1, List<T> list2){…}

void Swap(List<int> list1, List<int> list2){…}

 

六、泛型接口

凭是为泛型容器类,还是代表容器中元素的泛型类,定义接口是杀有因此底。把泛型接口及泛型类结合使用是还好的用法,比如用IComparable<T>而不IComparable,以避免值类型上之装箱和拆箱操作。.NET框架2.0看似库定义了几乎独新的泛型接口,以相当System.Collections.Generic中新容器类的使用。

 

   
当一个接口被指定为项目参数的牢笼时,只有实现该接口的品种可给作类型参数。下面的演示代码显示了一个从MyList<T>派生的SortedList<T>类。更多信息,请参见泛型概述。SortedList<T>增加了封锁where
T : IComparable<T>。

顿时使得SortedList<T>中的BubbleSort方法可使表中的要素的IComparable<T>.CompareTo方法。在斯事例中,表中的素是略近乎——实现IComparable<Person>的Person类。

 

using System;

using System.Collections.Generic;

 

//Type parameter T in angle brackets.

public class MyList<T>

{

    protected Node head;

    protected Node current = null;

 

// Nested type is also generic on T

    protected class Node         

    {

        public Node next;

//T as private member datatype.

        private T data;         

//T used in non-generic constructor.

        public Node(T t)        

        {

            next = null;

            data = t;

        }

        public Node Next

        {

            get { return next; }

            set { next = value; }

        }

//T as return type of property.

        public T Data           

        {

            get { return data; }

            set { data = value; }

        }

    }

    public MyList()

    {

        head = null;

    }

//T as method parameter type.

    public void AddHead(T t)    

    {

        Node n = new Node(t);

        n.Next = head;

        head = n;   

    }

    // Implement IEnumerator<T> to enable foreach

    // iteration of our list. Note that in C# 2.0

    // you are not required to implment Current and

    // GetNext. The compiler does that for you.

    public IEnumerator<T> GetEnumerator()

    {

        Node current = head;

 

        while (current != null)

        {

            yield return current.Data;

            current = current.Next;

        }

    }

}

 

 

public class SortedList<T> : MyList<T> where T :
IComparable<T>

{

    // A simple, unoptimized sort algorithm that

    // orders list elements from lowest to highest:

 

public void BubbleSort()

    {

 

        if (null == head || null == head.Next)

            return;

        bool swapped;

 

        do

        {

            Node previous = null;

            Node current = head;

            swapped = false;

 

            while (current.next != null)

            {

                //  Because we need to call this method, the SortedList

                //  class is constrained on IEnumerable<T>

                if (current.Data.CompareTo(current.next.Data) > 0)

                {

                    Node tmp = current.next;

                    current.next = current.next.next;

                    tmp.next = current;

 

                    if (previous == null)

                    {

                        head = tmp;

                    }

                    else

                    {

                        previous.next = tmp;

                    }

                    previous = tmp;

                    swapped = true;

                }

 

                else

                {

                    previous = current;

                    current = current.next;

                }

 

            }// end while

        } while (swapped);

    }

 

}

 

// A simple class that implements IComparable<T>

// using itself as the type argument. This is a

// common design pattern in objects that are

// stored in generic lists.

public class Person : IComparable<Person>

{

    string name;

    int age;

    public Person(string s, int i)

    {

        name = s;

        age = i;

    }

    // This will cause list elements

    // to be sorted on age values.

    public int CompareTo(Person p)

    {

        return age – p.age;

    }

    public override string ToString()

    {

        return name + “:” + age;

    }

    // Must implement Equals.

    public bool Equals(Person p)

    {

        return (this.age == p.age);

    }

}

 

class Program

{

    static void Main(string[] args)

    {

        //Declare and instantiate a new generic SortedList class.

        //Person is the type argument.

        SortedList<Person> list = new SortedList<Person>();

 

        //Create name and age values to initialize Person objects.

        string[] names = new string[]{“Franscoise”, “Bill”, “Li”,
“Sandra”, “Gunnar”, “Alok”, “Hiroyuki”, “Maria”, “Alessandro”, “Raul”};

        int[] ages = new int[]{45, 19, 28, 23, 18, 9, 108, 72, 30,
35};

 

        //Populate the list.

        for (int x = 0; x < 10; x++)

        {

            list.AddHead(new Person(names[x], ages[x]));

        }

        //Print out unsorted list.

        foreach (Person p in list)

        {

            Console.WriteLine(p.ToString());

        }

 

        //Sort the list.

        list.BubbleSort();

 

        //Print out sorted list.

        foreach (Person p in list)

        {

            Console.WriteLine(p.ToString());

        }

 

        Console.WriteLine(“Done”);

    }

}

 

 

好当一个品类指定多个接口作为约束,如下:

 

class Stack<T> where T : IComparable<T>,
IMyStack1<T>{}

 

 

一个接口可以定义多只类别参数,如下:

 

IDictionary<K,V>

 

接口及类似的后续规则一样:

//Okay.

IMyInterface : IBaseInterface<int>

//Okay.

IMyInterface<T> : IBaseInterface<T>

 

//Okay.

IMyInterface<T>: IBaseInterface<int>

//Error.

IMyInterface<T> : IBaseInterface2<T, U>

 

具体类可兑现封闭构造接口,如下:

class MyClass : IBaseInterface<string>

 

泛型类可兑现泛型接口或封闭构造接口,只要类的参数列表提供了接口需要的富有参数,如下:

//Okay.

class MyClass<T> : IBaseInterface<T>

//Okay.

class MyClass<T> : IBaseInterface<T, string>

 

泛型类、泛型结构,泛型接口都有所同等办法重载的规则。详细信息,请参见泛型方法。

 

七、泛型方法

 

泛型方法是信誉了类别参数的方,如下:

 

void Swap<T>( ref T lhs, ref T rhs)

{

  T temp;

  temp = lhs;

  lhs = rhs;

  rhs = temp;

}

 

 

脚的示范代码显示了一个缘int作为项目参数,来调用方法的例证:

 

int a = 1;

int b = 2;

//…

Swap<int>(a, b);

 

为堪忽略类型参数,编译器会失掉想其。下面调用Swap的代码和方的例子等价:

Swap(a, b);

 

 

静态方法和实例方法有同样的品类推断规则。编译器能够冲传入的道参数来测算类型参数;而一筹莫展单独根据约束还是回到值来判断。因此类推断对没参数的不二法门是无效的。类型推断发生在编译的时光,且当编译器解析重载方法标明之前。编译器对具有同名的泛型方法应用类型推断逻辑。在决定(resolution)重载的路,编译器只含有那些类型推断成功之泛型类。更多信息,请参见C#
2.0正经,20.6.4色参数推断

 

每当泛型方法吃,非泛型方法能看所在类中之项目参数,如下:

class MyClass<T>

{

  //…

  void Swap (ref T lhs, ref T rhs){…}

}

 

[JX1] 定义一个泛型方法,和该所于的好像具有同等之类参数;试图这样做,编译器会有警告CS0693。

 

class MyList<T>

{

// CS0693

    void MyMethod<T>{…}   

}

 

class MyList<T>

{

//This is okay, but not common.

    void SomeMethod<U>(){…}   

}

 

采取约束好在术被运用还多之类别参数的一定措施。这个本的Swap<T>称为SwapIfGreater<T>,它不得不采取实现了IComparable<T>的门类参数。

void SwapIfGreater<T>( ref T lhs, ref T rhs) where T:
IComparable<T>

{

  T temp;

  if(lhs.CompareTo(rhs) > 0)

    {

      temp = lhs;

      lhs = rhs;

      rhs = temp;

    }

}

 

泛型方法通过多个类型参数来重载。例如,下面的这些办法可以在同一个好像中:

void DoSomething(){}

void DoSomething<T>(){}

void DoSomething<T,U>(){}

 

 

八、泛型委托

不论是当类定义内还是类定义外,委托可以定义自己的种类参数。引用泛型委托的代码可以指定项目参数来创造一个查封构造类型,这同实例化泛型类或调用泛型方法一致,如下例所示:

 

public delegate void MyDelegate<T>(T item);

public void Notify(int i){}

//…

 

MyDelegate<int> m = new MyDelegate<int>(Notify);

 

C#2.0本来只新特性称为方法组转换(method group
conversion),具体代理及泛型代理项目且好运用。用方法组转换可以将地方一行写做简化语法:

MyDelegate<int> m = Notify;

 

于泛型类中定义的信托,可以同类似的点子一致地采用泛型类的品类参数。

 

class Stack<T>

{

T[] items;

      int index

//…

public delegate void StackDelegate(T[] items);

}

 

援委托的代码必须要指定所在类的项目参数,如下:

 

Stack<float> s = new Stack<float>();

Stack<float>.StackDelegate myDelegate = StackNotify;

 

 

泛型委托在概念基于典型设计模式的轩然大波时特意有因此。因为sender[JX2] ,而又为无用和Object相互转换。

 

public void StackEventHandler<T,U>(T sender, U eventArgs);

class Stack<T>

{

    //…

    public class StackEventArgs : EventArgs{…}

    public event StackEventHandler<Stack<T>, StackEventArgs>
stackEvent;

    protected virtual void OnStackChanged(StackEventArgs a)

    {

      stackEvent(this, a);

    }

}

class MyClass

{

  public static void HandleStackChange<T>(Stack<T> stack,
StackEventArgs args){…};

}

Stack<double> s = new Stack<double>();

MyClass mc = new MyClass();

s.StackEventHandler += mc.HandleStackChange;

 

 

九、泛型代码中的 default 关键字

 

在泛型类和泛型方法被见面面世的一个问题是,如何拿少省值赋给参数化类型,此时无法事先了解以下简单触及:

l        T将是值类型还是引用类型

l        如果T是值类型,那么T将是数值或组织

 

对此一个参数化类型T的变量t,仅当T是引用类型时,t = null语句子才是法定的;
t =
0但针对数值的管事,而针对组织则充分。这个题目的解决办法是故default关键字,它对援类型返回空,对值类型的数值型返回零。而对此组织,它将赶回结构每个成员,并冲成员是值类型还是引用类型,返回零或者空。下面MyList<T>类的例证显示了何等使用default关键字。更多信息,请参见泛型概述。

 

public class MyList<T>

{

    //…

        public T GetNext()

        {

            T temp = default(T);

            if (current != null)

            {

                temp = current.Data;

                current = current.Next;

            }

            return temp;

        }

}

 

十、 C++ 模板和 C# 泛型的分别

(未翻译)

 

C# Generics and C++ templates are both language features that provide
support for parameterized types. However, there are many differences
between the two. At the syntax level, C# generics are a simpler
approach to parameterized types without the complexity of C++ templates.
In addition, C# does not attempt to provide all of the functionality
that C++ templates provide. At the implementation level, the primary
difference is that C# generic type substitutions are performed at
runtime and generic type information is thereby preserved for
instantiated objects. For more information, see Generics in the Runtime.

 

The following are the key differences between C# Generics and C++
templates:

·                     C# generics do not provide the same amount of
flexibility as C++ templates. For example, it is not possible to call
arithmetic operators in a C# generic class, although it is possible to
call user defined operators.

·                     C# does not allow non-type template parameters,
such as template C<int i> {}.

·                     C# does not support explicit specialization; that
is, a custom implementation of a template for a specific type.

·                     C# does not support partial specialization: a
custom implementation for a subset of the type arguments.

·                     C# does not allow the type parameter to be used
as the base class for the generic type.

·                     C# does not allow type parameters to have default
types.

·                     In C#, a generic type parameter cannot itself be
a generic, although constructed types can be used as generics. C++ does
allow template parameters.

·                     C++ allows code that might not be valid for all
type parameters in the template, which is then checked for the specific
type used as the type parameter. C# requires code in a class to be
written in such a way that it will work with any type that satisfies the
constraints. For example, in C++ it is possible to write a function that
uses the arithmetic operators + and – on objects of the type parameter,
which will produce an error at the time of instantiation of the template
with a type that does not support these operators. C# disallows this;
the only language constructs allowed are those that can be deduced from
the constraints.

 

十一 、运行时遭受之泛型

Specialized generic types are created once for each unique value type
used as a parameter.

 

当泛型类还是泛型方法给编译为微软当中语言(MSIL)后,它所包含的第一数据定义了其的色参数。根据所为的种参数是值类型还是引用类型,对泛型类型所用底MSIL也是殊的。

   
当第一涂鸦因值类型作为参数来组织一个泛型类型,运行时用所提供的参数或在MSIL中正好位置为轮换的参数,来创造一个专用的泛型类型。[JX3] 

 

    例如,假要你的程序代码声名一个由于整型构成的库,如:

 

Stack<int> stack;

 

这儿,运行时用整型恰当地替换了她的种类参数,生成一个专用版本的堆栈。此后,程序代码再就此到整型栈时,运行时复用已创造的专用的库。下面的例子创建了点儿独整型栈的实例,它们同用一个Stack<int>代码实例:

 

Stack<int> stackOne = new Stack<int>();

Stack<int> stackTwo = new Stack<int>();

 

   
然而,如果由于其他一样栽值类型——如长整型或用户从定义的组织——作为参数,在代码的另外地方创造另一个库,那么运行时会转移另一个本子的泛型类型。这次是管长整型替换到MSIL中之贴切的职务。由于每个专用泛型类原本就是包含值类型,因此无需重新换。

 

   
对于引用类型,泛型的工作略有不同。当第一涂鸦用另外引用类型构造泛型类时,运行时于MSIL中创造一个专用泛型类,其中的参数为对象引用所替换。之后,每当用一个引用类型作为参数来实例化一个曾构造类型时,就忽略其类别,运行时复用先前开立的专用版本的泛型类。这恐怕是由有着的援的分寸都无异。

 

   
例如,假如你闹少数个援类型C++,一个Customer类和一个Order类;进一步假而你创造了一个Customer的库房:

 

Stack<Customer> customers;

 

   
此时,运行时生成一个专用版本的库,用于稍后储存对象的援,而非是储存数据。假如下一行代码创建了一个外一样种引用类型的库,名也Order:

 

Stack<Order> orders = new Stack<Order>();

 

   
和值类型不同,运行时并不曾为Order类型创建另一个储藏室的专用版本。相反,运行时创造了一个专用版本栈实例,并且变量orders指为这个实例。如果之后是单排创建Customer类型的库房的代码:

 

customers = new Stack<Customer>();

 

跟之前因Order类型创建的库房一样,创建了专用仓库的别一个实例,并且其中所涵盖的指针指为同块大小与Customer类一致的内存。由于不同程序中引用类型的数据差异甚酷,而编译器只吗援类型的泛型类创建一个专用类,因此C#本着泛型的落实大地降落了代码膨胀。

   
此外,当用路参数实现一个泛型C#类时,想知道它是恃类型或者引用类型,可以当运行时经过反射确定它们的实事求是类型以及它们的色参数。

 

 

 

十二 、基础类库中之泛型

   
2.0本子的.NET框架类库提供了一个新的命名空间,System.Collections.Generic,其中蕴蓄了有的曾可以以的泛型容器类和血脉相通的接口。和初版本的.NET框架提供的非泛型容器类相比,这些看似以及接口更迅速且是种类安全之。在统筹、实现自定义的容器类之前,请而着想是不是采用要接续所列出类中之一个。

 

   
下面的报表列有了新的泛型类和接口,旁边是相应之非泛型类和接口。在有的地方要特别注意,如List<T>和Dictionary<T>,新泛型类的行(behavior)与她所替换的非泛型类小不同,也无净匹配。更详细的始末,请参见System.Collections.Generic的文档

 

 

 

泛型类或接口
描述
对应的非泛型类型
Collection<T>
ICollection<T>
为泛型容器提供基类
CollectionBase
ICollection
Comparer<T>
IComparer<T>
IComparable<T>
比较两个相同泛型类型的对象是否相等、可排序。
Comparer
IComparer
IComparable
Dictionary<K, V>
IDictionary<K,V>
表示用键组织的键/值对集合。
Hashtable
IDictionary
Dictionary<K, V>.KeyCollection
表示Dictionary<K, V>中键的集合。
None.
Dictionary<K, V>.ValueCollection
表示Dictionary<K, V>中值的集合。
None.
IEnumerable<T>
IEnumerator<T>
表示可以使用foreach 迭代的集合。
IEnumerable
IEnumerator
KeyedCollection<T, U>
表示有键值的集合。
KeyedCollection
LinkedList<T>
表示双向链表。
None.
LinkedListNode<T>
表示LinkedList<T>中的节点。
None.
List<T>
IList<T>
使用大小可按需动态增加的数组实现 IList 接口
ArrayList
IList
Queue<T>
表示对象的先进先出集合。
Queue
ReadOnlyCollection<T>
为泛型只读容器提供基类。
ReadOnlyCollectionBase
SortedDictionary<K, V>
 表示键/值对的集合,这些键和值按键排序并可按照键访问,实现IComparer<T>接口。
SortedList
Stack<T>
表示对象的简单的后进先出集合。
Stack