【bf88必发唯一官网】中的泛型

by admin on 2019年5月16日

一、泛型概述

      泛型类和泛型方法兼复用性、类型安全和高效能于一身,是与之对应的非泛型的类和艺术所不比。泛型分布用于容器(collections)和对容器操作的艺术中。.NET
Framework
二.0的类库提供3个新的命名空间System.Collections.Generic,个中含有了有的新的依靠泛型的容器类。

  1. 泛型的可变类型参数:平时用T,但也足以用肆意非关键字和保留字;
  2. 怀有的可变类型T在编写翻译时,都施用占位符的花样,在运行时将由实际传入的品类来替换的兼具的点位符;

 

C#泛型,

转自:

2、泛型的长处

针对前期版本的通用语言运转时和C#言语的受制,泛型提供了三个解决方案。以前类型的泛化(generalization)是靠类型与大局基类System.Object的交互调换成兑现。
.NET Framework
基础类库的ArrayList容器类,正是这种局限的三个例子。ArrayList是2个很有益的容器类,使用中无需改动就能够积攒任何引用类型或值类型。

ArrayList list = new ArrayList();
list.Add(1);
list.Add(175.50);
list.Add("hello kitty");

double sum = 0;
foreach(int value in list)
{
     sum += value;
}

缺点:

     
便利是有代价的,那亟需把别的1个投入ArrayList的引用类型或值类型都隐式地向上转变到System.Object。尽管那个要素是值类型,那么当进入到列表中时,它们必须棉被服装箱;当再一次收复它们时,要拆箱。类型转变和装箱、拆箱的操作都下落了品质;在必须迭代(iterate)大容器的图景下,装箱和拆箱的震慑恐怕非常斐然。另二个受制是贫乏编写翻译时的花色检查,当3个ArrayList把任何类型都更改为Object,就不可能在编写翻译时堤防客户代码中就像sum+=vlaue这样的荒唐;

在System.Collections.Generic命名空间中的泛型List<T>容器里,同样是把成分出席容器的操作,类似这样:

    List<int> listInt = new List<int>();
     listInt.Add(100);
     listInt.Add(200);
     listInt.Add(123.112); //编译报错
     listInt.Add("heel");  //编译报错

    double sum = 0;
     foreach (int value in list)
     {
         sum += value;
     }

与ArrayList比较,在客户代码中唯1扩充的List<T>语法是宣称和实例化中的类型参数。代码略微复杂的报恩是,你创立的表不仅仅比ArrayList更安全,而且鲜明地进一步便捷,特别当表中的成分是值类型的时候。

一、泛型概述

     
泛型类和泛型方法兼复用性、类型安全和高效用于一身,是与之对应的非泛型的类和方法所比不上。泛型遍布用于容器(collections)和对容器操作的诀窍中。.NET
Framework
二.0的类库提供2个新的命名空间System.Collections.Generic,个中饱含了1部分新的依附泛型的容器类。

 

泛型(generic)是C#言语二.0和通用语言运营时(CLCR-V)的3个新特色。泛型为.NET框架引入了项目参数(type
parameters)的概念。类型参数使得设计类和艺术时,不必明确一个或多少个有血有肉参数,其的现实性参数可顺延到客户代码中申明、完毕。这意味使用泛型的连串参数T,写一个类MyList<T>,客户代码能够如此调用:MyList<int>,
MyList<string>或
MyList<MyClass>。那制止了运营时类型转变或装箱操作的代价和高风险。

3、泛型的门类参数

     
泛型类型或泛型方法的概念中,类型参数是1个占位符(placeholder),平日为三个大写字母(也能够应用任性非关键字和保留字的名字),如T。在客户代码证明、实例化该类型的变量时,把T替换为客户代码所钦定的数据类型。泛型类,如泛型中付出的List<T>类,不能用作as-is,原因在于它不是一个当真的品类,而更像是一个品类的蓝图。要利用MyList<T>,客户代码必须在尖括号内钦命一个项目参数,来声称并实例化一个已构造类型(constructed
type)。那么些特定类的等级次序参数能够是编写翻译器度和胆识别的其余项目。能够创设任性数量的已构造类型实例,每一种使用差异的类型参数,如下:

List<int> listInt = new List<int>();
List<float> listFloat = new List<float>();
List<String> listString = new List<String>();

 

二、泛型的亮点

本着开始时代版本的通用语言运转时和C#言语的受制,泛型提供了三个缓慢解决方案。以前类型的泛化(generalization)是靠类型与全局基类System.Object的互动转变到完毕。
.NET Framework
基础类库的ArrayList容器类,正是这种局限的二个例子。ArrayList是三个很便利的容器类,使用中不供给改动就足以储存任何引用类型或值类型。

ArrayList list = new ArrayList();
list.Add(1);
list.Add(175.50);
list.Add("hello kitty");

double sum = 0;
foreach(int value in list)
{
     sum += value;
}

缺点:

     
便利是有代价的,那亟需把其余一个参与ArrayList的引用类型或值类型都隐式地向上调换到System.Object。如若这个元素是值类型,那么当进入到列表中时,它们必须棉被服装箱;当再一次收复它们时,要拆箱。类型调换和装箱、拆箱的操作都下降了品质;在必须迭代(iterate)大容器的情形下,装箱和拆箱的影响大概特别威名赫赫。另3个受制是贫乏编写翻译时的档案的次序检查,当一个ArrayList把任何类型都更动为Object,就不或许在编写翻译时防止客户代码中就像是sum+=vlaue这样的失实;

在System.Collections.Generic命名空间中的泛型List<T>容器里,同样是把成分加入容器的操作,类似那样:

    List<int> listInt = new List<int>();
     listInt.Add(100);
     listInt.Add(200);
     listInt.Add(123.112); //编译报错
     listInt.Add("heel");  //编译报错

    double sum = 0;
     foreach (int value in list)
     {
         sum += value;
     }

与ArrayList比较,在客户代码中有一无二扩张的List<T>语法是宣称和实例化中的类型参数。代码略微复杂的回报是,你成立的表不唯有比ArrayList更安全,而且明显地进一步便捷,特别当表中的成分是值类型的时候。

 

肆、泛型类型参数的牢笼

泛型提供了下列各样约束:

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

但是制类型参数:

  1. 不可能运用!=和==对可变类型的实例进行相比较,因为无法有限援助具体的系列参数辅助这个运算符;
  2. 它们能够与System.Object相互调换,也可显式地调换到任何接口类型;
  3. 可以与null比较。假诺一个极致制类型参数与null相比,当此类型参数为值类型时,相比的结果总为false。

无类型约束:当自律是一个泛型类型参数时,它就叫无类型约束(Naked
type constraints)。

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

在地点的躬行实践中,
Add方法的左右文中的T,正是3个无类型约束;而List类的光景文中的T,则是1个极端制类型参数。

无类型约束也足以用在泛型类的概念中。注意,无项目约束一定也要和其它项目参数一同在尖括号中声明:

//naked type constraint

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

因为编写翻译器只以为无类型约束是从System.Object承接而来,所以富含无类型约束的泛型类的用途丰硕星星。当您愿意强制四个门类参数具备继续关系时,可对泛型类应用无类型约束。

3、泛型的档案的次序参数

     
泛型类型或泛型方法的概念中,类型参数是二个占位符(placeholder),常常为贰个大写字母(也得以利用任性非关键字和保留字的名字),如T。在客户代码证明、实例化该类型的变量时,把T替换为客户代码所钦命的数据类型。泛型类,如泛型中提交的List<T>类,无法用作as-is,原因在于它不是一个确实的品种,而更像是贰个体系的蓝图。要动用MyList<T>,客户代码必须在尖括号内内定一个品类参数,来声称并实例化3个已构造类型(constructed
type)。那么些特定类的连串参数能够是编译器识别的别的类型。能够创造自便数量的已构造类型实例,各类使用不一致的类型参数,如下:

List<int> listInt = new List<int>();
List<float> listFloat = new List<float>();
List<String> listString = new List<String>();

 

 

五、泛型类

泛型类包装了不对准任何特定数据类型的操作。泛型类常用于容器类,如链表、哈希表、栈、队列、树等等。那几个类中的操作,如对容器增多、删除元素,不论所蕴藏的多寡是何体系型,都执行大致千篇一律的操作。

习感到常,从二个已有的具体类来创建泛型类,并每便把叁个等级次序改为品种参数,直至到达常常和可用性的最棒平衡。当创制你自身的泛型类时,须要重视思量的事项有:

  • 什么样项目应泛化为品种参数。一般的原理是,用参数表示的类型越来越多,代码的八面见光和复用性也就越大。过多的泛化会招致代码难以被别的的开辟职员精通。
  • 假使有约束,那么类型参数须求怎样约束。叁个完好无损的习于旧贯是,尽或然使用最大的封锁,同时确认保障能够管理全数须求管理的品种。比方,假如你精晓您的泛型类只妄图选择引用类型,那么就利用那一个类的羁绊。这样可防止范无意中选取值类型,同时能够对T使用as运算符,并且检查空引用;
  • 把泛型行为放在基类中如故子类中。泛型类可以做基类。同样非泛型类的希图中也应思量这或多或少。泛型基类的存在延续规则;
  • 是还是不是达成3个或多少个泛型接口。比方,要统一计划三个在依据泛型的器皿中创制成分的类,恐怕须要贯彻类似IComparable<T>的接口,个中T是此类的参数。

对此二个泛型类Node<T>,客户代码既可内定三个品种参数来创设二个查封构造类型(Node<int>),也可保留项目参数未钦命,譬喻钦点1个泛型基类来创造开放构造类型(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> {…}

继续自开放结构类型的泛型类,必须内定参数类型和封锁:

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) {…}

 

4、泛型类型参数的约束

泛型提供了下列各种约束:

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

最棒制类型参数:

无类型约束:当自律是二个泛型类型参数时,它就叫无类型约束(Naked type
constraints)。

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

在上头的示范中,
Add方法的内外文中的T,正是1个无类型约束;而List类的上下文中的T,则是二个极端制类型参数。

无类型约束也得以用在泛型类的概念中。注意,无项目约束一定也要和其他类型参数一同在尖括号中扬言:

//naked type constraint

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

因为编写翻译器只以为无类型约束是从System.Object承接而来,所以富含无类型约束的泛型类的用处充足少于。当您愿意强制多个类型参数具备持续关系时,可对泛型类使用无类型约束。

 

6、泛型接口

当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>

泛型类、泛型结构,泛型接口都具备同样措施重载的条条框框。

【bf88必发唯一官网】中的泛型。 

五、泛型类

泛型类包装了不对准任何特定数据类型的操作。泛型类常用于容器类,如链表、哈希表、栈、队列、树等等。那几个类中的操作,如对容器加多、删除成分,不论所蕴藏的数量是何系列型,都施行差不离一致的操作。

普通,从二个已部分具体类来创建泛型类,并每回把三个项目改为项目参数,直至到达常常和可用性的特级平衡。当创设你本身的泛型类时,须要入眼思考的事项有:

  • 如何类型应泛化为品种参数。一般的规律是,用参数表示的连串越多,代码的布帆无恙和复用性也就越大。过多的泛化会变成代码难以被其它的开辟人士精晓。
  • 若是有约束,那么类型参数必要哪些约束。三个爱不释手的习于旧贯是,尽大概选拔最大的束缚,同时保障能够拍卖全体需求管理的类型。举例,借使您领会你的泛型类只筹划动用引用类型,那么就接纳那么些类的自律。那样能够幸免无意中运用值类型,同时可以对T使用as运算符,并且检查空引用;
  • 把泛型行为放在基类中依旧子类中。泛型类能够做基类。同样非泛型类的规划中也应思索那或多或少。泛型基类的持续规则;
  • 是还是不是落到实处一个或多个泛型接口。举个例子,要规划三个在依据泛型的器皿中创立成分的类,或然须要贯彻类似IComparable<T>的接口,当中T是此类的参数。

对此一个泛型类Node<T>,客户代码既可钦点3个品类参数来创设三个查封构造类型(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> {…}

持续自开放结构类型的泛型类,必须钦定参数类型和封锁:

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) {…}

 

 

7、泛型方法

泛型方法是信誉了花色参数的点子,如下:

void Swap<T>(ref T lhs, ref T rhs)
{
     T temp;
     temp = lhs;
     lhs = rhs;
     rhs = temp;
}

上面包车型客车言传身教代码显示了1个以int作为项目参数,来调用方法的例证:

int a = 1;
int b = 2;
//…
Swap<int>(a, b);

也足以忽略类型参数,编写翻译器会去推想它。上边调用Swap的代码与地点的事例等价:

Swap(a, b);

静态方法和实例方法有着同样的连串预计规则。编写翻译器能够基于传入的艺术参数来测度类型参数;而可望不可即独立依照约束或重回值来剖断。由此类型揣摸对从未参数的格局是不行的。类型估量产生在编写翻译的时候,且在编译器解析重载方法标明从前。编写翻译器对负有同名的泛型方法运用项目估算逻辑。在决定(resolution)重载的阶段,编写翻译器只包括那叁个类型推测成功的泛型类。

在泛型类中,非泛型方法能访问所在类中的类型参数:

class List<T>
{
     void Swap(ref T lhs, ref T rhs) { … }
}

在泛型类中,定义二个泛型方法,和其所在的类具备一样的体系参数;试图那样做,编写翻译器会时有爆发警告CS0693。

class List<T>
{
     void Swap<T>(ref T lhs, ref T rhs) {  }
}

warning CS0693: 类型参数“T”与外部类型“List<T>”中的类型参数同名

在泛型类中,定义3个泛型方法,可定义1个泛型类中未定义的品种参数:(有的时候用,一般合作约束使用)

class List<T>
{
     void Swap<U>(ref T lhs, ref T rhs) {  }   //不常用

    void Add<U>(List<U> items) where U : T{}  //常用
}

泛型方法通过三个类型参数来重载。举个例子,下边包车型地铁那么些情势能够献身同一个类中:

void DoSomething() { }
void DoSomething<T>() { }
void DoSomething<T, U>() { }

六、泛型接口

当几个接口被钦点为品种参数的自律时,唯有完结该接口的体系可被作为类型参数。

能够在三个项目内定四个接口作为约束,如下:

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>

泛型类、泛型结构,泛型接口都有着同等措施重载的条条框框。

 

目录

 

7、泛型方法

泛型方法是人气了体系参数的法子,如下:

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)重载的级差,编写翻译器只含有那八个类型推测成功的泛型类。

在泛型类中,非泛型方法能访问所在类中的类型参数:

class List<T>
{
     void Swap(ref T lhs, ref T rhs) { … }
}

在泛型类中,定义三个泛型方法,和其所在的类具备同等的类型参数;试图那样做,编写翻译器会生出警告CS06玖三。

class List<T>
{
     void Swap<T>(ref T lhs, ref T rhs) {  }
}

warning CS0693: 类型参数“T”与外部类型“List<T>”中的类型参数同名

在泛型类中,定义贰个泛型方法,可定义一个泛型类中未定义的档期的顺序参数:(不经常用,一般合营约束使用)

class List<T>
{
     void Swap<U>(ref T lhs, ref T rhs) {  }   //不常用

    void Add<U>(List<U> items) where U : T{}  //常用
}

泛型方法通过多个项目参数来重载。举例,上边包车型大巴那几个主意能够投身同1个类中:

void DoSomething() { }
void DoSomething<T>() { }
void DoSomething<T, U>() { }

C# 中的泛型. 1

八、泛型中的default关键字

在泛型类和泛型方法中会出现的2个主题素材是,怎样把缺省值赋给参数化类型,此时不能够先行掌握以下两点:

  1. T将是值类型依然引用类型
  2. 假如T是值类型,那么T将是数值还是协会

对此贰个参数化类型T的变量t,仅当T是引用类型时,t
= null语句才是法定的; t =
0只对数值的有效性,而对组织则特别。这几个主题素材的化解办法是用default关键字,它对引用类型重返空,对值类型的数值型重临零。而对于组织,它将赶回结构每种成员,并依据成员是值类型依然引用类型,重返零或空。

class GenericClass<T>
{
     T GetElement()
     {
         return default(T);
     }
}

 

一、泛型概述. 2

8、泛型中的default关键字

在泛型类和泛型方法中会出现的3个主题材料是,怎么样把缺省值赋给参数化类型,此时不能事先理解以下两点:

对此3个参数化类型T的变量t,仅当T是援引类型时,t = null语句才是官方的;
t =
0只对数值的可行,而对组织则13分。这几个标题标化解办法是用default关键字,它对引用类型再次回到空,对值类型的数值型重返零。而对此组织,它将再次来到结构各个成员,并依赖成员是值类型照旧引用类型,再次来到零或空。

class GenericClass<T>
{
     T GetElement()
     {
         return default(T);
     }
}

1、泛型概述 泛型类和泛型方法兼 复用性
、 类型安全 和 高功能于一身,是与之对应的非泛型的类和办法所比不上。泛型布满用于…

二、泛型的优点. 5

3、泛型类型参数【bf88必发唯一官网】中的泛型。. 7

4、类型参数的羁绊. 8

五、泛型类. 11

陆、泛型接口. 13

7、泛型方法. 19

八、泛型委托. 21

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

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

十一 、运维时中的泛型. 25

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

 

 

 

一、泛型概述

   
泛型类和泛型方法兼复用性、类型安全和高效能于寥寥,是与之相应的非泛型的类和方法所比不上。泛型布满用于容器(collections)和对容器操作的措施中。.NET框架二.0的类库提供一个新的命名空间System.Collections.Generic,其中涵盖了一些新的依赖泛型的容器类。要探究新的泛型容器类(collection
classes)的亲自去做代码,请参见基础类库中的泛型。当然,你也能够创立协和的泛型类和办法,以提供您自个儿的泛化的方案和设计情势,那是系列安全且快速的。上边包车型大巴亲自去做代码以五个粗略的泛型链表类作为示范。(大多气象下,推荐使用由.NET框架类库提供的List<T>类,而不是创设和煦的表。)类型参数T在多处选拔,具体项目一般在那个地点来指明表桐月素的花色。类型参数T有以下三种用法:

l        在AddHead方法中,作为艺术参数的品类。

l        在公私措施GetNext中,以及嵌套类Node的
Data属性中作为重返值的花色。

l        在嵌套类中,作为个体成员data的类别。

 

在意一点,T对嵌套的类Node也是卓有成效的。当用3个切实可行类来贯彻MyList<T>时——如MyList<int>——各个出现过的T都要用int取代。

 

bf88必发唯一官网 1bf88必发唯一官网 2

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;
            }
        }
    }

View Code

 

下边包车型大巴以身作则代码演示了客户代码如何行使泛型类MyList<T>,来创制一个平头表。通过简单地改成参数的项目,很轻松改写上边包车型客车代码,以创立字符串或别的自定义类型的表。

 

bf88必发唯一官网 3bf88必发唯一官网 4

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");
        }
    }

View Code

二、泛型的帮助和益处

针对开始时期版本的通用语言运营时和C#言语的受制,泛型提供了一个缓慢解决方案。从前类型的泛化(generalization)是靠类型与大局基类System.Object的互相调换到促成。.NET框架基础类库的ArrayList容器类,就是这种局限的一个例证。ArrayList是3个很有益于的容器类,使用中无需退换就能够积存任何引用类型或值类型。

 

bf88必发唯一官网 5bf88必发唯一官网 6

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

View Code

而是这种便利是有代价的,那要求把别的多少个加盟ArrayList的引用类型或值类型都隐式地向上改换到System.Object。若是这几个要素是值类型,那么当进入到列表中时,它们必须被装箱;当再度收复它们时,要拆箱。类型调换和装箱、拆箱的操作都下降了品质;在必得迭代(iterate)大容器的处境下,装箱和拆箱的震慑或许那些眼看。

 

另二个受制是贫乏编写翻译时的门类检查,当一个ArrayList把其余类型都更动为Object,就不可能在编写翻译时防备客户代码类似这样的操作:

 

bf88必发唯一官网 7bf88必发唯一官网 8

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;
}

View Code

固然如此完全合法,并且一时是假意那样成立三个包蕴不一样等级次序成分的器皿,可是把string和int变量放在1个ArrayList中,大致是在制作错误,而以此错误直到运维的时候才会被察觉。

 

在1.0版和1.1版的C#言语中,你唯有通过编写制定本身的特定类型容器,工夫防止.NET框架类库的容器类中泛化代码(generalized
code)的危险。当然,因为如此的类不可能被其它的数据类型复用,也就错过泛型的长处,你必须为各样需求仓库储存的档案的次序重写该类。

 

ArrayList和其余一般的类真正须要的是壹种渠道,能让客户代码在实例化此前钦赐所需的一定数据类型。那样就无需向上类型转变为Object,而且编写翻译器可以同时拓展项目检查。换句话说,ArrayList必要2个种类参数。那多亏泛型所提供的。在System.Collections.Generic命名空间中的泛型List<T>容器里,同样是把成分参加容器的操作,类似那样:

bf88必发唯一官网 9bf88必发唯一官网 10

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.");

View Code

与ArrayList相比较,在客户代码中独占鳌头扩张的List<T>语法是宣称和实例化中的类型参数。代码略微复杂的回报是,你创制的表不止比ArrayList更安全,而且显明地更加飞快,非常当表中的成分是值类型的时候。

 

三、泛型类型参数

   

   
在泛型类型或泛型方法的定义中,类型参数是叁个占位符(placeholder),经常为一个大写字母,如T。在客户代码评释、实例化该类型的变量时,把T替换为客户代码所内定的数据类型。泛型类,如泛型概述中提交的MyList<T>类,不能够用作as-is,原因在于它不是四个当真的门类,而更像是一个品种的蓝图。要选用MyList<T>,客户代码必须在尖括号内钦命三个连串参数,来声称并实例化贰个已构造类型(constructed
type)。这么些特定类的花色参数能够是编译器度和胆识其余其他类型。能够创造放肆数量的已构造类型实例,每种使用区别的种类参数,如下:

 

bf88必发唯一官网 11bf88必发唯一官网 12

MyList<MyClass> list1  = new MyList<MyClass>();
MyList<float> list2 = new MyList<float>();
MyList<SomeStruct> list3 = new MyList<SomeStruct>();

View Code

 

   
在那些MyList<T>的实例中,类中出现的各种T都就要运营的时候被项目参数所代表。依赖那样的交替,大家仅用定义类的代码,就创立了多个单身的类型安全且高效的指标。有关CLCR-V实施替换的详细信息,请参见运行时中的泛型。

 

4、类型参数的牢笼

 

若要检查表中的一个要素,以分明它是不是合法或是还是不是能够与其余因素相比较,那么编写翻译器必须确定保障:客户代码中或然出现的有所类型参数,都要帮衬所需调用的操作或措施。这种保险是经过在泛型类的定义中,应用一个或多个约束而收获的。3个封锁类型是一种基类约束,它打招呼编写翻译器,只有这一个类别的指标或从这么些类型派生的靶子,可被用作类型参数。一旦编写翻译器获得那样的保障,它就同意在泛型类中调用这几个类型的办法。上下文关键字where用以完结约束。上边包车型地铁亲自去做代码表明了选择基类约束,为MyList<T>类扩大效益。

 

bf88必发唯一官网 13bf88必发唯一官网 14

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;
  }
}

View Code

 

自律使得泛型类能够利用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,
被称为Infiniti制类型参数(unbounded type
parameters)。Infiniti制类型参数有以下规则:

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,则是2个最为制类型参数。

 

无类型约束也足以用在泛型类的概念中。注意,无项目约束一定也要和任何项目参数一齐在尖括号中声称:

//naked type constraint

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

 

因为编写翻译器只以为无类型约束是从System.Object承接而来,所以富含无类型约束的泛型类的用处丰硕零星。当您期望强制三个连串参数具有继续关系时,可对泛型类使用无类型约束。

 

五、泛型类

 

 

泛型类包装了不对准其余特定数据类型的操作。泛型类常用于容器类,如链表、哈希表、栈、队列、树等等。这一个类中的操作,如对容器加多、删除成分,不论所蕴藏的多少是何类别型,都进行差不多一样的操作。

 

对很多气象,推荐使用.NET框架二.0类库中所提供的容器类。有关使用这一个类的详细音讯,请参见基础类库中的泛型。

 

平凡,从三个已有个别具体类来成立泛型类,并每一遍把二个档案的次序改为品种参数,直至到达平时和可用性的一级平衡。当创设你本身的泛型类时,必要注重考虑的事项有:

l       
哪些类型应泛化为品种参数。一般的法则是,用参数表示的项目越来越多,代码的灵活性和复用性也就越大。过多的泛化会招致代码难以被其余的开采职员通晓。

l       
倘若有约束,那么类型参数需求什么约束。四个特出的习贯是,尽恐怕选用最大的束缚,同时确定保障能够拍卖全部必要管理的档次。比如,纵然您领会您的泛型类只筹划采取引用类型,那么就动用那个类的封锁。这样能够免范无意中选取值类型,同时能够对T使用as运算符,并且检查空引用。

l       
把泛型行为放在基类中照旧子类中。泛型类能够做基类。一样非泛型类的安顿性中也应思念这点。泛型基类的延续规则    

l       
是还是不是达成三个或两个泛型接口。举个例子,要统一希图贰个在依据泛型的器皿中开创成分的类,大概必要贯彻类似IComparable<T>的接口,当中T是此类的参数。

 

泛型概述中有3个简练泛型类的事例。

 

项目参数和自律的规则对于泛型类的一举一动(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){…}

 

6、泛型接口

不论是是为泛型容器类,仍旧表示容器中元素的泛型类,定义接口是很有用的。把泛型接口与泛型类结合使用是更加好的用法,比如用IComparable<T>而非IComparable,以幸免值类型上的装箱和拆箱操作。.NET框架2.0类库定义了多少个新的泛型接口,以合营System.Collections.Generic中新容器类的应用。

 

   
当一个接口被钦定为品种参数的束缚时,唯有完成该接口的品类可被当做类型参数。下边包车型大巴以身作则代码彰显了3个从MyList<T>派生的SortedList<T>类。越多音讯,请参见泛型概述。SortedList<T>扩大了约束where
T : IComparable<T>。

那使得SortedList<T>中的BubbleSort方法能够利用表中的因素的IComparable<T>.CompareTo方法。在这些例子中,表中的元素是粗略类——达成IComparable<Person>的Person类。

 

bf88必发唯一官网 15bf88必发唯一官网 16

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");
    }
}

View Code

 

能够在2个连串钦点四个接口作为约束,如下:

 

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>

 

泛型类、泛型结构,泛型接口都装有一样措施重载的规则。详细新闻,请参见泛型方法。

bf88必发唯一官网, 

7、泛型方法

 

泛型方法是信誉了等级次序参数的措施,如下:

 

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#
二.0专门的学业,20.陆.4类型参数预计

 

在泛型方法中,非泛型方法能访问所在类中的类型参数,如下:

class MyClass<T>

{

  //…

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

}

 

[JX1] 定义二个泛型方法,和其所在的类具备同样的项目参数;试图那样做,编写翻译器会生出警告CS06九三。

 

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>(){}

 

 

八、泛型委托

甭管在类定义内依旧类定义外,委托能够定义本身的花色参数。引用泛型委托的代码能够钦命项目参数来创设八个封闭构造类型,那和实例化泛型类或调用泛型方法1致,如下例所示:

 

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

public void Notify(int i){}

//…

 

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

 

C#二.0版有个新本性称为方法组调换(method group
conversion),具体代理和泛型代理项目都能够选取。用方法组调换能够把上面一行写做简化语法:

MyDelegate<int> m = Notify;

 

在泛型类中定义的委托,能够与类的方法1致地行使泛型类的类型参数。

 

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

 

在泛型类和泛型方法中会出现的3个题目是,如何把缺省值赋给参数化类型,此时无法先行明白以下两点:

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] 

 

    举个例子,倘令你的程序代码声名1个由整型构成的栈,如:

 

Stack<int> stack;

 

那会儿,运营时用整型恰本地替换了它的体系参数,生成三个专项使用版本的栈。此后,程序代码再用到整型栈时,运营时复用已开立的专项使用的栈。上面包车型地铁例证创设了五个整型栈的实例,它们共用三个Stack<int>代码实例:

 

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

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

 

   
然则,如若由另一种值类型——如长整型或用户自定义的结构——作为参数,在代码的任啥地点方创立另多少个栈,那么运维时会生成另3个版本的泛型类型。此次是把长整型替换来MSIL中的适当的职分。由于各种专项使用泛型类原本就包括值类型,因而无需再改变。

 

   
对于引用类型,泛型的劳作略有差异。当第三次用其余引用类型构造泛型类时,运转时在MSIL中创制二个专项使用泛型类,个中的参数被对象引用所替换。之后,每当用3个引用类型作为参数来实例化三个已构造类型时,就大要其种类,运营时复用先前创制的专项使用版本的泛型类。那可能是由于具有的引用的轻重缓急都1致。

 

   
比如,假若你有四个引用类型,3个Customer类和二个Order类;进一步假如你创设了1个Customer的栈:

 

Stack<Customer> customers;

 

   
此时,运营时生成3个专项使用版本的栈,用于稍后储存对象的引用,而不是积攒数据。假如下1行代码成立了三个另一种引用类型的栈,名叫Order:

 

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

 

   
和值类型区别,运转时并从未为Order类型创立另二个栈的专用版本。相反,运转时创设了叁个专项使用版本栈实例,并且变量orders指向那个实例。如若现在是单排成立Customer类型的栈的代码:

 

customers = new Stack<Customer>();

 

和从前以Order类型创造的栈同样,创设了专项使用栈的另2个实例,并且其中所涵盖的指针指向一块大小与Customer类一致的内部存款和储蓄器。由于不一样程序间引用类型的数目差距异常的大,而编写翻译器只为引用类型的泛型类创设一个专项使用类,由此C#对泛型的贯彻一点都不小地下降了代码膨胀。

   
别的,当用类型参数完成二个泛型C#类时,想知道它是指类型恐怕引用类型,可以在运作时经过反射明确它的真实类型和它的连串参数。

 

 

 

十二 、基础类库中的泛型

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

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图