C语言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中创造一个专用泛型类,其中的参数为对象引用所替换。之后,每当用一个援类型作为参数来实例化一个已经构造类型时,就大意其品种,运行时复用先前创立的专用版本的泛型类。这或许是出于所有的援的大大小小都一样。

 

   
例如,假如你有些许独援类型,一个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