C#中的泛型

 

转载:C#中的泛型

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

泛型(generic)是C#语言2.0和通用语言运转时(CLMurano)的多个新特点。泛型为.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)的言传身教代码,请参见基础类库中的泛型。当然,你也得以成立自身的泛型类和办法,以提供你协调的泛化的方案和设计格局,那是项目安全且高效的。上边包车型的士示范代码以3个简练的泛型链表类作为示范。(多数景观下,推荐使用由.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#言语的受制,泛型提供了2个化解方案。从前类型的泛化(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供给2个档次参数。那多亏泛型所提供的。在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更安全,而且肯定地进一步迅速,尤其当表中的成分是值类型的时候。

 

③ 、泛型类型参数

   

   
在泛型类型或泛型方法的概念中,类型参数是1个占位符(placeholder),经常为二个大写字母,如T。在客户代码评释、实例化该类型的变量时,把T替换为客户代码所钦命的数据类型。泛型类,如泛型概述中提交的MyList<T>类,不能用作as-is,原因在于它不是贰个真正的门类,而更像是2个档次的蓝图。要选拔MyList<T>,客户代码必须在尖括号内钦命1个种类参数,来声称并实例化八个已构造类型(constructed
type)。那一个特定类的档次参数能够是编写翻译器度和胆识别的别样项目。能够创设任意数量的已构造类型实例,每个使用差别的品类参数,如下:

 

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

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

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

 

   
在那个MyList<T>的实例中,类中出现的各样T都将在运行的时候被项目参数所代替。依靠那样的轮换,我们仅用定义类的代码,就创立了五个单身的品种安全且不慢的对象。有关CL兰德酷路泽执行替换的详细音信,请参见运维时中的泛型。

 

肆 、类型参数的约束

 

若要检查表中的1个元素,以明确它是或不是合法或是或不是足以与其他因素相比较,那么编译器必须保险:客户代码中可能出现的兼具种类参数,都要支持所需调用的操作或措施。那种保证是经过在泛型类的定义中,应用三个或三个约束而赢得的。一个封锁类型是一种基类约束,它打招呼编写翻译器,唯有那一个项目标靶子或从那一个项目派生的对象,可被作为类型参数。一旦编写翻译器获得这么的管教,它就同目的在于泛型类中调用那一个项目标主意。上下文关键字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的目的。

 

同1个品类参数可采取三个约束。约束自身也能够是泛型类,如下:

 

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相比。假诺3个最好制类型参数与null相比,当此类型参数为值类型时,相比的结果总为false。

 

 

无类型约束

当自律是3个泛型类型参数时,它就叫无类型约束(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       
借使有约束,那么类型参数必要什么样约束。1个地道的习惯是,尽只怕选取最大的牢笼,同时确认保证可以拍卖全数必要处理的花色。例如,假若您领悟您的泛型类只打算利用引用类型,那么就使用那一个类的束缚。那样能够防患无意中央银行使值类型,同时能够对T使用as运算符,并且检查空引用。

l       
把泛型行为放在基类中还是子类中。泛型类能够做基类。同样非泛型类的筹划中也应考虑那一点。泛型基类的延续规则    

l       
是不是达成四个或多少个泛型接口。例如,要统一筹划一个在依照泛型的容器中成立成分的类,恐怕须要达成类似IComparable<T>的接口,个中T是此类的参数。

 

泛型概述中有一个简单易行泛型类的例子。

 

类型参数和平条约束的规则对于泛型类的表现(behavior)有一对私人住房的熏陶,——特别是对于后续和分子可访问性。在认证那一个题材前,精通一些术语13分重点。对于3个泛型类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){…}

 

6、泛型接口

不论是是为泛型容器类,依然表示容器兰月素的泛型类,定义接口是很有用的。把泛型接口与泛型类结合使用是更好的用法,比如用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”);

    }

}

 

 

能够在3个项目钦点多个接口作为约束,如下:

 

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] 定义3个泛型方法,和其所在的类具有相同的种类参数;试图那样做,编写翻译器会发出警告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;

    }

}

 

泛型方法通过多个类型参数来重载。例如,上面包车型地铁这几个办法能够置身同1个类中:

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 关键字

 

在泛型类和泛型方法中会出现的1个题材是,怎么样把缺省值赋给参数化类型,此时无法先行精晓以下两点:

l        T将是值类型依旧引用类型

l        要是T是值类型,那么T将是数值照旧组织

 

对于2个参数化类型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;

 

此刻,运行时用整型安妥地替换了它的类型参数,生成贰个专用版本的栈。此后,程序代码再用到整型栈时,运营时复用已成立的专用的栈。上面的例证成立了四个整型栈的实例,它们共用3个Stack<int>代码实例:

 

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

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

 

   
不过,假诺由另一种值类型——如长整型或用户自定义的布局——作为参数,在代码的其余省方创设另3个栈,那么运营时会生成另三个本子的泛型类型。这一次是把长整型替换成MSIL中的适当的岗位。由于每种专用泛型类原本就富含值类型,由此不供给再转移。

 

   
对于引用类型,泛型的劳作略有不一样。当第壹遍用别的引用类型构造泛型类时,运维时在MSIL中创立二个专用泛型类,当中的参数被对象引用所替换。之后,每当用2个引用类型作为参数来实例化二个已构造类型时,就马虎其体系,运营时复用先前开创的专用版本的泛型类。那可能是由于具备的引用的轻重都一致。

 

   
例如,借使你有三个引用类型,叁个Customer类和叁个Order类;进一步若是你成立了2个Customer的栈:

 

Stack<Customer> customers;

 

   
此时,运营时生成叁个专用版本的栈,用于稍后囤积对象的引用,而不是储存数据。倘诺下一行代码创制了3个另一种引用类型的栈,名为Order:

 

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

 

   
和值类型差别,运营时并没有为Order类型成立另二个栈的专用版本。相反,运营时制造了1个专用版本栈实例,并且变量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