C#C#中的泛型

 

转载:C#中的泛型

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

泛型(generic)是C#言语2.0和通用语言运营时(CLRubicon)的一个新特色。泛型为.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)的示范代码,请参见基础类库中的泛型。当然,你也足以创立和谐的泛型类和章程,以提供您本身的泛化的方案和设计格局,那是系列安全且神速的。上面包车型地铁示范代码以1个不难易行的泛型链表类作为示范。(多数动静下,推荐使用由.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是1个很方便的容器类,使用中无需改变就能够储存任何引用类型或值类型。

 

//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.”);

//…

 

然则这种便宜是有代价的,那供给把别的1个到场ArrayList的引用类型或值类型都隐式地向上转换来System.Object。若是那几个成分是值类型,那么当进入到列表中时,它们必须棉被服装箱;当再一次收复它们时,要拆箱。类型转换和装箱、拆箱的操作都降低了质量;在必得迭代(iterate)大容器的事态下,装箱和拆箱的影响只怕尤其众所周知。

 

另三个受制是贫乏编写翻译时的花色检查,当2个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须要3个项目参数。那正是泛型所提供的。在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更安全,而且显明地进一步火速,越发当表中的成分是值类型的时候。

 

③ 、泛型类型参数

   

   
在泛型类型或泛型方法的定义中,类型参数是2个占位符(placeholder),平时为1个大写字母,如T。在客户代码申明、实例化该品种的变量时,把T替换为客户代码所钦赐的数据类型。泛型类,如泛型概述中提交的MyList<T>类,不可能用作as-is,原因在于它不是3个当真的品种,而更像是叁个品种的蓝图。要利用MyList<T>,客户代码必须在尖括号内钦点二个系列参数,来声称并实例化2个已构造类型(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对象可能3个持续自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比较。若是三个无限制类型参数与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       
如若有约束,那么类型参数需求哪些约束。1个优秀的习惯是,尽大概接纳最大的封锁,同时保险能够处理全体供给处理的项目。例如,假诺你知道你的泛型类只打算选拔引用类型,那么就利用那个类的羁绊。那样能够幸免无意中采纳值类型,同时能够对T使用as运算符,并且检查空引用。

l       
把泛型行为放在基类中依旧子类中。泛型类能够做基类。同样非泛型类的陈设性中也应考虑那点。泛型基类的三番陆次规则    

l       
是还是不是贯彻多少个或多个泛型接口。例如,要设计多个在根据泛型的容器中成立成分的类,大概须求达成类似IComparable<T>的接口,在那之中T是此类的参数。

 

泛型概述中有一个简约泛型类的例证。

 

连串参数和平条约束的规则对于泛型类的作为(behavior)有一些私人住房的震慑,——越发是对于持续和分子可访问性。在印证这么些题材前,精通一些术语12分至关心重视要。对于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”);

    }

}

 

 

能够在1个档次钦赐多个接口作为约束,如下:

 

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;

}

 

 

上边包车型大巴言传身教代码展现了3个以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;

    }

}

 

泛型方法通过多个类型参数来重载。例如,上面包车型地铁那些措施能够放在同3个类中:

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也是分裂的。

   
当第3遍以值类型作为参数来组织一个泛型类型,运维时用所提供的参数或在MSIL中稳当地点被替换的参数,来创建3个专用的泛型类型。[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;

 

   
此时,运维时生成2个专用版本的栈,用于稍后储存对象的引用,而不是储存数据。假若下一行代码创制了1个另一种引用类型的栈,名为Order:

 

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

 

   
和值类型差别,运营时并没有为Order类型创立另一个栈的专用版本。相反,运营时创设了1个专用版本栈实例,并且变量orders指向这几个实例。假使之后是单排创制Customer类型的栈的代码:

 

customers = new Stack<Customer>();

 

和在此之前以Order类型创制的栈一样,创立了专用栈的另三个实例,并且在那之中所包罗的指针指向一块大小与Customer类一致的内部存款和储蓄器。由于区别程序间引用类型的数额差别十分大,而编写翻译器只为引用类型的泛型类创造2个专用类,因而C#对泛型的达成极大地降落了代码膨胀。

   
别的,当用类型参数完毕叁个泛型C#类时,想通晓它是指类型只怕引用类型,能够在运作时经过反射分明它的真实性类型和它的类别参数。

 

 

 

十二 、基础类库中的泛型

   
2.0版的.NET框架类库提供了2个新的命名空间,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

 

相关文章