初学泛型

 

前言

前些天同学告知我他老师告诉她,泛型是概念一种通用的不二法门,至于为啥用泛型他的答案还是定义一种通用的形式,当时自家也绝非学,还不懂啊,毕竟人家是统计机专业的科班生,我当然没有发言权,只是怀疑的记录了这多少个荒唐的定论打算未来看看是不是对的,这两天算是把泛型学了一晃,其实它定义的不只是艺术,自己深感他就是一种标准代码执行效用的模版,这样极不容易出错,并把我们常用的箱拆箱操作过程给省了,提高了编译器的实施效用,。至于何以用泛型那一个我一般已经表达白了,泛型多用于集合类操作,我在此处随意的拿出C#概念的ArrayList的 Add方法的概念大家一起看看:

publicvirtual int Add (

Object value

)

可以看出我们得以往ArrayList里添加任何类型的要素,要是有一个string类型的str字符串它在添加到ArrayList lib的长河中实际上举办了装箱操作,即object value=str;那么我们在得到这么些值得时候必须这么使用string s=(string) lib[0];无法遗漏(string)否则会指示您,

不当 1 不能将项目“object”隐式转换为“string”。存在一个显式转换(是否缺少强制转换?)

不过你假如这样写:int a =(int) lib[0];程序在运行前是无力回天报错的,只要一旦运行就会出现非常,而这个不当都可以选择泛型轻松搞定,让你在编译时就可以发现错误,具体的就看你怎么理解了,我在篇章里没有写任何概念的语法格式,因为这些既没必要说,那是常识,出来混这多少个自然是要通晓的,呵呵。玩笑啦……欢迎新手同学来我的博客交流学习,我也冀望你们可以为本人敞开胸怀,因为我既不是总括机专业的学生,甚至上了大学本身曾经不复是理工类的学员。我下意识扎进了文科专业去叱咤风云啦,哎,那么些文科专业名字太有点科学技术的意味了。我欣赏 ……..

 

 关于泛型的多少个举足轻重概念:

 

泛型类型形式参数(类型形参):是泛型类型或泛型方法等概念中的占位符。类型形参一般以大写字母 T 起初,假设只有一个项目形参,

一般用 T 来表示。

 构造泛型类型(构造类型):是为泛型类型定义的泛型类型参数指定项目得到的结果。

 泛型类型实际参数(类型实参):是替换泛型形参的此外类型。

封锁:是加在泛型类型参数上的限定。使用泛型类型的客户端不可能替换不餍足这多少个约束的品种参数。

 

泛型类:

泛型类型可以按照项目形参的数据举行“重载”,也就是多少个门类讲明可以运用同一的标识符,只要这六个声明具有不同数量的类型参数

 即可。例如:

 class A { };

class A<T> { };

class A<U, V> { };

 

此刻一经大家在概念一个 class A<C>; 或 class A<A, B> { }; 就会报错,为何吗,原因很简短,这就是自我眼前提到的这多少个大写字母本

 身没有多大意思,只是一个(等待替换的不为人知实际参数的)占位符即类型参数。非泛型类类讲明的中指定的基类或者基接口能够是构造类型但不可能不是查封构造类型。(也就.NET 的 CLR 的 JIT 将泛型 IL 和元数据转换为本机代码,并在该过程大校类型形参替换为项目实参)不过无法是

 类型形参,而在基类或者基接口的效用域中能够分包类型形参。下边我做下详细表明,例如:

 

usingSystem; 

 

namespaceGeneric

{

class A<U, V>

{

public virtual void F1(U a, V b)

{

Console.WriteLine(“F1中涵盖的参数的 CTS 类型为:\n{0}\n{1}”,

a.GetType(), b.GetType());

}

}

interface I<T>

{

 

T F2(T x);

//基类或者基接口的效能域中可以分包类型形参

 

}

class ChildTest1:A<string, string>

{

//重写基类的 F1方法

public override void F1(string a, string b)

{

Console.WriteLine(a+b);

}

}

class ChidTest2:A<string ,int>,I<string>

{

//实现基接口中的方法

public string F2(string x)

{

return (“F2包含的参数值为:”+x);

}

//该类隐藏了基类的 F1格局

public new void F1(string a, int b)

{

Console.WriteLine(“ChidTest2类隐藏了基类的 F1办法”);

}

}

//测试类

class Test

{

static void Main(string[] args)

{

//泛型类 A 只有先河化类型参数后才有意义

A<string, int> a = new A<string, int>();//注意:其中 A<string,

int>为构造类型,也就是开首化类型形参

ChildTest1 cd1 = new ChildTest1();

ChidTest2 cd2 = new ChidTest2();

a.F1(“123”,123);

cd1.F1(“ChildTest1类重写了基类的”,”F1主意”);

cd2.F1(“123”,123);

Console.WriteLine(cd2.F2(“123”));

Console.ReadKey(true);

}

}

}

 

在地方的代码中自我只是介绍了非泛型类的后续,那么泛型类的后续是不是平等啊?这些我们可以先自己想一想,我在讲到泛型接口的时候

 会提到,上面继续说一下啊泛型类的分子的特性。泛型类中所有的成员都可以一贯作为构造类型或者构造类型的一局部来使用兼容类enclosed class)中的类型形参。当公共语言运行时选择一定的封闭构造类型时,所出现的各种项目形参都被替换该构造类型提供的体系实参。例如:

 

usingSystem;

  

namespaceGeneric

{

class A<T>

{

//定义多个品种字段

public T a; //作为构造类型的一有些(成员)使用类型形参 T

public A<T> b = null;//直接当做构造类型使用类型形参 T

//实例构造函数,其中 this 是 A<T>的实例

public A(T x)

 

{

this.a = x;

this.b = this;

}

}

//测试类

class Test

{

static void Main(string[] args)

{

A<string> A1 = new A<string>(“Hello World”);

Console.WriteLine(A1.a);

Console.WriteLine(A1.b.a);

A<int> A2 = new A<int>(123);

Console.WriteLine(A1.a);

Console.WriteLine(A1.b.a);

Console.ReadKey(true);

}

}

}

 

或许这边有的同学不明了自己说的查封构造类型,我面前提到了构造类型,我在此间在解释一下,例如A<T>和A<string>都是构造类型,我们将(使用一个或多少个类型形参的构造类型)A<T>称为开放构造类型;将(不实用类型形参(即利用实参)的构造类型)A<string>称为封闭构造类型。此外对于地点的代码我得以如此表明:即构造类型成员可以直接或部分当做开放构造类型使用项目形参,可以从来或局部当作

封闭构造类型使用项目实参。

泛型接口

 我原先提到过类型形参只是一个(等待替换的未知实际参数的)占位符。那么只要如此接口interface I<T>,interface I<A> , interface I<C>其实是相同的,同时和泛型类一样,可以遵照项目形参的数目举办“重载”,也就是四个档次讲明可以采取相同的标识符,只要这三个注明

怀有不同数额的项目参数。那么我们如此想,假设我要用一个类实现interfaceI<string>和
I <string> 和

 

interface I <int>

 

interface I<T>

{

void F();

}

,我们是否足以采纳形如:

class A<U, V> : I<U>, I<V> //之后交替为interface I <string> 和interface I <int>

{

void I<U>.F()

{

}

void I<V>.F()

{

}

}

 

这般编译器便会指示:

错误 1

 

“Generic.A<U,V>”无法同时落实“Generic.I<U>”和“Generic.I<V>”,原因是它们对于一些品种形参替换可以拓展联合

也就是说,当使用泛型类实现泛型接口必须试所有可能的泛型接口的构造类型(例如I<U>)保持唯一,否则编译器无法确定该为某个泛型接口的显示成员调用那一个情势。 可能觉得I<U>就是I<V>)。然则大家得以将不同继承级别指定的接口举办统一。例如:

interface I<T>

{

void F();

}

class Base<U> : I<U>

{

void I<U>.F()

{

Console.WriteLine(“I<U>.F()”);

}

}

class Derived<U, V> : Base<U>, I<V>

{

void I<V>.F()

{

Console.WriteLine(“I<U>.F()”);

}

}

class Test

{

public static void Main()

{

I<int> test1 =new Base<int>();

I<int> test2= new Derived<string, int>();

test1.F();

test2.F();

Console.ReadKey();

Console.ReadKey();

}

}

从地点的代码可以看看,尽管Derived<U,V> 同时实现了I<U>和I<V> , test2.F() 这 里 将 调 用 I<V>() 的 方 法 , 实 际 上 实 在Derived<U,V>中重复实现了I<int>.这里我们再回到地方我说泛型类的时候,泛型类继承的表征,这里一看代码便已明了,泛型类继承的基类或者基接口可以是开放的构造类型也足以使封闭的构造类型(我已测试,那么些我们也可以测试一下、)。

泛型方法

泛型方法可以在类,结构,接口注明中扬言,这个类,结构,接口本身可以是泛型或者非泛型。

1.就算泛型方法在泛型类型中扬言,则方法体可以同时引述该措施的项目形参和含有该措施的宣示(泛型类,结构,接口)的

  类型形参。

 2.类型形参可以用作再次来到类型或形参的体系。

 
3.措施的类型形参的称号不可以与同一方法中的普通形参的称呼相同。

下边我对下面的题材逐一解释:

class A<T>

{

public static void Print<U, V>(U a, V b, T c)

{

Console.WriteLine(“{0}\n{1}\n{2}”, a.GetType(), b.GetType(), c.GetType());

}

}

class Test

{

public static void Main()

{

A<int>.Print<string, decimal>(“str”, 125.25m, 15);

Console.ReadKey();

}

}

对应问题 1

 

class A

{

public static U Print<U, V>(U a, V b)

{

return a;

}

}

class Test

{

public static void Main()

{

Console.WriteLine( A.Print<string, byte>(“str”, 125));

Console.ReadKey();

}

}

 

对应问题2

 

public static U Print<U, V>(U U, V b)倘使大家这里安装这么,语法没有不当,然则编译器运行

时会指示:

 

错误 1

“U”: 参数或一些变量不可能与办法类型形参同名

对应问题3

除此以外泛型方法除了非泛型方法的署名(由艺术的名称和他的每一个形参(按从左到右的逐条)的档次和序列(值,引用,输出)组成。)要素外,泛型方法还包括泛型类型形参的数码和类型形参的序号地方按从左到右),记住前面说过的品类形参只是个占位符。例如:

T F<T>(T a, int b)

{

return a;

}

void F (){ }

string F(string a,int b){};

}

下边的代码由于措施名相同,不过不会报错,为啥呢,这就提到到方法的重载策略,不过一旦出现:

public void F<U>(U a, int b) { },那么就会报错,因为这六个泛型类型形参的数量和连串形参的序号地方都是相同的。要是自己在这样改一下:

public void F<U>(int b,U a )

重载策略的衍生,其实是一律的法则。

泛型委托

此地普通的泛型委托和普通委托同一很粗略,我来试着测试一下匿名模式是不是也一如既往简单好了。

public delegate T Degle<T>(int a, T b);

class Test

{

public static void Main()

{

Degle<string> dg = delegate(int a, string b)

{

return b;

};

Console.WriteLine(dg(125, “使用了匿名格局创设泛型委托”));

Console.ReadKey();

}

}

 

约束

在概念泛型类时,可以对客户端代码可以在实例化类时用来项目参数的品各样类举行限制,假若客户端代码使用某个约束不容许的档次来实例化类,就会暴发编译时不当。这种范围称为约束。 约束是利用

where 上下文关键字指定的。下表列出了六系列型的束缚:

约束

说明

T:结构

类型参数必须是值类型。可以指定除 Nullable 以外的任何值

型。有关更多信息,请参见使用可空类型((C#C# 编程指南)。

T:类

类型参数必须是引用类型,包括任何类、接口、委托或数组型。

T:new()

类型参数必须具有无参数的公共构造函数 。当与其他约束一起用

用时,,new()new() 约束必须最后指定。

T:<基类名名>>

类型参数必须是指定的基类或派生自指定的基类。

T:<接口名称称>>

类型参数必须是指定的接口或实现指定的接口 。可以指定多个接

口约束。约束接口也可以是泛型的。

T:U

为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U

供的参数。这称为裸类型约束。

 

假定要检泛型列表中的某个项以确定它是不是管用,或者将它与此外某个项举办相比较,则编译器必须在早晚水准上保证它需要调用的运算符或格局将境遇客户端代码可能指定的其他类型参数的辅助。这种保证是经过对泛型类定义应用一个或多少个约束拿到的。例如,基类约束告诉编译器:仅此类型的对象或之后类型派生的目的才可用作类型参数。一旦编译器有了这一个保险,它就可知允许在泛型类中调用该类型的法门。首先我们先看一个(不应用约束的)例子:

using System;

using System.Collections;

namespace Generic

{

public class Student

{

private string name;

public string Name

{

set

{

name = value;

}

get

{

return name;

}

}

public Student(string s)

{

name = s;

}

}

public class Grade<T> //定义一个年级类

ArrayList names = new ArrayList(10);

public void Add(T t)

{

names.Add(t.Name);//那里便会出错

}

public void Display()

{

foreach (string s in names)

{

Console.WriteLine(s);

}

}

}

public class Test

{

public static void Main()

{

Grade<Student> gn = new Grade<Student>();

gn.Add(new Student(“小明”));

gn.Add(new Student(“小花”));

gn.Add(new Student(“阿猫”));

gn.Add(new Student(“阿狗”));

Console.WriteLine(“一年级的校友有:”);

gn.Display();

}

}

}

 

运行就会唤醒:

错误 1

“T”不包含“Name”的定义,并且找不到可承受类型为“T”的首先个参数的扩大方法“Name”(是否缺少 using 指令或程序集引用?)

 也就是说,大家在行使 T 为实在形参占位的时候,编译器运行到names.Add(t.Name);

例到底有没有Name属性,或者那个Name到底是字段仍旧属性呢?程序是一步一步走的,他从未人的逻辑那样跳步完成某个推理,所以这里就会报错,因而我们就应有提前报告编译器这里类型形参T所能代表的切实可行界定,这样就足以解决问题了。因而这里就引出了约束来轻松的缓解这一问题。大家只需要修改一句:public class Grade<T> where T:Student 大家在 Grade<T>的类中追加了约束,修改未来程序就可以便已经过,因为我们曾经告知编译器 Grade<T>的 T 是 Student 类或者派生自 Student 类的,所以可以选拔Stundent.Name 属性。

 usingSystem;

usingSystem.Collections;

namespaceGeneric

{

public class Student

{

private string name;

public string Name

{

set

{

name = value;

}

get

{

return name;

}

}

public Student(string s)

{

name = s;

}

}

public class Grade<T> where T:Student

{

ArrayList names = new ArrayList(10);

public void Add(T t)

{

names.Add(t.Name);//这里便会出错

}

public void Display()

{

foreach (string s in names)

{

Console.WriteLine(s);

}

}

}

public class Test

{

public static void Main()

{

Grade<Student> gn = new Grade<Student>();

gn.Add(new Student(“小明”));

gn.Add(new Student(“小花”));

gn.Add(new Student(“阿猫”));

gn.Add(new Student(“阿狗”));

Console.WriteLine(“一年级的同室包括”);

gn.Display();

}

}

}

可以对同样档次的参数应用三个约束,并且封锁自己可以是泛型类型,例如:

public class Grade<T> where T:Student,IStudent,System.IComparable<T>,new()

{

//…

}

 

这边首要一点的就是new(),new约束指定泛型类阐明中的任何类型参数都必须有国有的无参数构造函数。如果要运用new约束,则该类型无法为架空类型。当泛型类创立项目标新实例,请将new约束应用于类型参数,如下边的言传身教所示:

classItemFactory<T>where T :new()

{

public T GetNewItem()

{

return new T();

}

}

当与任何约束共同使用时,new() 约束必须最终指定:

publicclassItemFactory2<T>

where T : IComparable, new()

{

}

 通过自律类型参数,能够增加约束类型及其继承层次结构中的所有品类所支撑的同意操作和艺术调用的数码。由此,在统筹泛型类或措施时,假若要对泛型成员进行除简单赋值之外的其他操作或调用System.Object 不协理的另外措施,您将急需对该品种参数应用约束。

动用 where T : class 约束时,避免对品种参数使用 == 和 != 运算符,因为这么些运算符仅测试引用同一性而不测试值相等性。固然在作为参数的门类中重载这多少个运算符也是这么。上边的代码表达了这一点;即便 String 类重载 == 运算符,输出也为 false。

public static void OpTest<T>(T s, T t) where T : class

{

System.Console.WriteLine(s == t);

}

static void Main()

{

string s1 = “foo”;

System.Text.StringBuilder sb = new System.Text.StringBuilder(“foo”);

string s2 = sb.ToString();

OpTest<string>(s1, s2); //输出 false

}

这种状态的因由在于,编译器在编译时只晓得 T 是引用类型,由此必须运用对具有引用类型都灵验的默认运算符。假使必须测试值相等性,指出的形式是还要采用 where T : IComparable<T> 约束,并在将用以社团泛型类的任何类中落实该接口。

自律四个参数

可以对多个参数应用约束,并对一个参数应用四个约束,如下边的演示所示:

class Base { }

class Test<T, U>

where U : structwhere T : Base, new() { }

未绑定的花色参数

从不约束的类型参数(如公共类 SampleClass<T>{} 中的 T)称为未绑定的项目参数。未绑定的项目参数具有以下规则:不可以应用 = 和 == 运算符,因为无法担保具体品种参数能支撑这些运算符。可以在它们与 System.Object 之间往来转换,或将它们显式转换为此外接口类型。可以将它们与 null 举行相比较。将未绑定的参数与 null 举行比较时,即使类型参数为值类型,则该相比较将始终再次来到 false。

裸类型约束

 

作为约束的泛型类型参数称为裸类型约束。当有着温馨的品类参数的成员函数必须将该参数约束为带有类型的门类参数时,裸类型约束很

用,如上边的演示所示:

class List<T>

{

void Add<U>(List<U> items) where U : T {/*…*科学技术,/}

}

 

在上头的演示中,T 在 Add 方法的上下文中是一个裸类型约束,而在 List 类的左右文中是一个未绑定的类型参数。裸类型约束仍可以够在泛型类定义中拔取。注意,还非得已经和其他任何类型参数一起在尖括号中宣称了裸类型约束:

//naked type constraint

public class 萨姆(Sam)pleClass<T, U, V> where T : V { }泛型类的裸类型约束的效能特别简单,因为编译器除了假诺某个裸类

型约束派生自 System.Object 以外,不会做其他任何即使。在盼望强制多少个品种参数之间的存续关系的气象下,可对泛型类使用裸类型约束。

应用泛型集合类

泛型最广大的用途是泛型集合,从.NET2.0版类库开头提供一个新的命名空间 Sysstem.Collection.Generic,其中涵盖了多少个新的基于泛型的集合类。使用泛型集合类可以提供更高的类别安全性,在一些意况下还足以提供更好的特性,尤其是在蕴藏值类型时,这种优势更显然。

回顾一下常用的汇集类型

标题

说明

数组集合类型

描述可以让数组作为集合来进行处理的数组功能。

ArrayList 和

List 集合类型

描述泛型列表和非泛型列表(最常用的集合类型)的功能。

Hashtable 和

Dictionary 集 合

类型

描述基于哈希的泛型和非泛型字典类型的功能。

已排序的集合类型

描述提供列表和集的排序功能的类。

队列集合类型

描述泛型和非泛型队列的功能。

堆栈集合类型

描述泛型和非泛型堆栈的功能。

HashSet 集合类型

描述泛型 System.Collections.Generic.HashSet(OfT) 集合

类型。

HashSet 和 LINQ

Set 运算

描述 System.Collections.Generic.HashSet(OfT) 集合类型

提供的 Set 操作以及 LINQ Set 操作。

集合和数据结构

讨论 .NET Framework 中提供的各种集合类型,包括堆栈、队列、

列表、数组和结构。

.NET Framework

中的泛型

描述泛型功能,包括由 .NET Framework 提供的泛型集合、委托和

接口。 提供指向有关 C#、Visual Basic 和 Visual C++ 的功能

文档和支持技术(如反射)的链接。

 

常用的非泛型集合类及其相应的泛型集合类

 

ArrayList

List<T>

Hashtable

Dictionary<TKey,TValue>

Queue

Queue<T>

Stack

Stack<T>

SortedList

SortedList<T>

正如二种泛型集合类型没有相应的非泛型类型:

 

LinkedList 是一个通用的链接链表,它提供运算复杂为 O(1)的插入和移除操作。

SortedDictionary 是一个排序的字典,其插入和寻找操作的演算复杂度为 O(log n),这使它变成 SortedList 的要命有效的代表类

型。KeyedCollection是在乎列表和字典里面的良莠不齐类型,它提供了一种存储包含自己健的对象的不二法门。

一般景色下,提议采取泛型集合,因为这样可以拿走类型安全的裨益而不需要从集合类型派生并贯彻特定的积极分子。另外如若集合素为值类型,泛型集合类型的性能一般优于对应的非泛型集合类型(并优化从非泛型集合类型派生的档次),因为运用泛型时不必元素举办装箱。对应的非泛型集合和泛型集合的效率相似基本相同,不过一些泛型类型具有在非泛型集合类型中没用的服从。例如List<T>类(对应用非泛型ArrayList类)具有众多收受泛型委托(如允许指定搜索列表的措施的Predicate委托、表示操作每个列元素的Action委托和同意定义类型之间转换的Converter委托)的不二法门。List<T>类允许指定自己的用来排序和搜索列表的ICompare泛型接口实现。

下边看下List<T>和Dictionary<TKey,电视机alue>的程式代码:

using System;
using System.Collections.Generic;
namespace Generic_List
{
 class Test
 {
 static void Main(string[] args)
 {
 List<string> li =new List<string>();
 //与ArrayList一样默认容量大小为0,我在前方的篇章还涉嫌过ArrayList构造函数的频率问题
 //用法也基本等同
 Console.WriteLine(“Capacity:{0}\n”,li.Capacity);
 li.Add(“小沈阳”);
 li.Add(“赵本山”);
 li.Add(“陈佩斯”);
 li.Add(“柳岩”);
 foreach (string sin li)
 {
 Console.WriteLine(s);
 }
 Console.WriteLine(“Capacity:{0}\n Count:{1}”,li.Capacity,li.Count);
 Console.WriteLine(@”Contains(“”柳岩””) is {0}”, li.Contains(“柳岩”));
 Console.WriteLine(@”Insert(2,””宋丹丹””)”);
 li.Insert(2,”宋丹丹”);
 for (int i =0; i < li.Count; i++)
 {
 Console.WriteLine(li[i]);
 }
 Console.WriteLine(“Capacity:{0}\n Count:{1}”, li.Capacity, li.Count);
 Console.WriteLine(“Remove(\”陈佩斯\”)”);
 li.Remove(“陈佩斯”);
 foreach (string sin li)
 {
 Console.WriteLine(s);
 }
 //这里我们再次注脚一下Capacity是否自动减小,实际是false
 Console.WriteLine(“Capacity:{0}\n Count:{1}”, li.Capacity, li.Count);
 li.TrimExcess();//功能等价于ArrayList的TrimToSize
 Console.WriteLine(“使用TrimExcess函数后,Capacity={0}”,li.Capacity);
 Console.WriteLine(“使用Clear方法删除所有因素”);
 li.Clear();
 Console.WriteLine(“Capacity:{0}\n Count:{1}”, li.Capacity, li.Count);
 Console.ReadKey();
 }
 }
}

//////////////////////////////////////////////////////////////////////////////

usingSystem;
usingSystem.Collections.Generic;

namespaceGeneric_Dictionary
{
 class Test
 {
 static void Main(string[] args)
 {
 Dictionary<string,string> doc =new Dictionary<string,string>();
 doc.Add(“操蛋曾某”,”初中数学老师”);
 doc.Add(“蛋痛梁某”,”初中语文老师”);
 doc.Add(“无奈黄某”,”高中乌克兰(Crane)语老师”);
 doc.Add(“邪恶徐某”,”无良缺德先生”);
 //虽然试图添加一个早就存在的健,Add方法会抛出一个分外
 try
 {
 doc.Add(“邪恶徐某”,”老师中的垃圾车”);
 }
 catch (ArgumentException)
 {
 Console.WriteLine(“已经存在健->邪恶徐某”);
 }
 Console.WriteLine(“对应健->邪恶徐某的值(描述)为:{0}”, doc[“邪恶徐某”]);
 //借使使用索引器来赋值时,尽管健已经存在,那么就会修改对应的值,而不会吸引那么些
 doc[“邪恶徐某”] =”老师中的垃圾车”;
 Console.WriteLine(“对应健->邪恶徐某的值(描述)为:{0}”, doc[“邪恶徐某”]);
 //使用索引器添加一个因素。
 doc[“xxxx”] =”大学老师”;
 try
 {
 Console.WriteLine(“对应doc[\”肖某\”]的值为:{0}”, doc[“肖某”]);
 }
 catch (KeyNotFoundException)
 {
 Console.WriteLine(“无对应值!”);
 }
 //当程序必须通常尝试得到字典中不设有的键值时,可以使用TryGetValue方法
 //作为一种更使得的检索值方法。
 string value =””;
 if (doc.TryGetValue(“ABC”,out value))
 {
 Console.WriteLine(“对应健\”ABC\”的值为{0}.”,value);
 }
 else
 {
 Console.WriteLine(“对应健\”ABC\”的值不存在”);
 }
 //插入元素前可以先用ContainKey来判断健是否业已存在
 if (!doc.ContainsKey(“ABC”))
 {
 doc.Add(“ABC”,”It’s a Test”);
 Console.WriteLine(“已经添加了对应健\”ABC\”的值{0}\n”,doc[“ABC”]);
 }
 Console.WriteLine(“Remove(\”ABC\”)”);
 doc.Remove(“ABC”);
 if (!doc.ContainsKey(“ABC”))
 {
 Console.WriteLine(“没有找到ABC健”);
 }
 foreach (KeyValuePair<string,string> k_vin doc)
 {
 Console.WriteLine(“Key={0},Value={1}”,k_v.Key,k_v.Value);
 }
 //值的会聚
 Dictionary<string,string>.ValueCollection Vco = doc.Values;
 Console.WriteLine();
 foreach (string sin Vco)
 {
 Console.WriteLine(“Value={0}”,s);
 }
 //键的会聚
 Dictionary<string,string>.KeyCollection Kco = doc.Keys;
 Console.WriteLine();
 foreach (string sin Kco)
 {
 Console.WriteLine(“Key={0}”, s);
 }
 Console.ReadKey();
 }
 }
}

 

自定义泛型集合类

与自定义的非泛型集合类一样,自定义泛型集合类也必须实现System.Collection.Generic命名空间中的IEnumerable<T>和IEnummerator<T>

接口,由于IEnumerable<T>和IEnummerator<T>分别继承了IEnumerable和IEnummerator接口,由此,实现 IEnumerable<T>接口的类同时,也无法不兑现IEnumerable接口,实现IEnummerator<T>接口的类同时,也亟须贯彻IEnummerator接口。

 usingSystem;
usingSystem.Collections.Generic;
usingSystem.Collections;

namespaceBookstore
{
 class store<T>:IEnumerable<T>,IEnumerator<T>
 {
 private string exceptionInfo =”索引无效,请保管调用了MoveNext”;
 private int index = -1;
 protected List<T> books;
 public store()
 {
 books=new List<T>() ;
 }
 public void Add(T book)
 {
 books.Add(book);
 }
 public void Remove(T book)
 {
 books.Remove(book);
 }
 //实现IEnumerator接口
 public bool MoveNext()
 {
 return(++index<books.Count);
 }
 public void Reset()
 {
 index = -1;
 }
 public T Current
 {
 get
 {
 if (0 <= index && index < books.Count)
 return books[index];
 else
 throw new IndexOutOfRangeException(exceptionInfo);
 }
 }
 //一般来说,非泛型的Current方法可以省略的调用泛型Current方法
 object System.Collections.IEnumerator.Current
 {
 get
 {
 return Current;
 }
 }
 public IEnumerator<T> GetEnumerator()
 {
 Reset();
 return this;
 }
 //一般来说,非泛型的GetEnumerator方法可以简简单单的调用泛型GetEnumerator方法
 IEnumerator System.Collections.IEnumerable.GetEnumerator()
 {
 return GetEnumerator();
 }
 public void Dispose()
 {
 }
 }
 class Test
 {
 static void Main(string[] args)
 {
 store<string> store =new store<string>();
 store.Add(“语文”);
 store.Add(“数学”);
 store.Add(“英语”);
 Console.WriteLine(“Store包含以下图书:”);
 foreach (string sin store)
 {
 Console.WriteLine(s);
 }
 Console.ReadKey();
 }
 }
}

 

泛型迭代器

与非泛型迭代器唯一的区分就是,泛型迭代器的回来类型必须是System.Collection.Generic.IEnumble<T>或者System.Collection.Generic.IEnumerator<T>,例如:

 usingSystem;
usingSystem.Collections.Generic;

namespaceGeneric_yield
{
 public class BookStore<T>
 {
 protected List<T> books;
 public BookStore()
 {
 books =new List<T>();
 }
 public void Add(T book)
 {
 books.Add(book);
 }
 public void Remove(T book)
 {
 books.Remove(book);
 }
 public IEnumerator<T> GetEnumerator()
 {
 foreach (T bookin books)
 {
 yield return book;
 }
 }
 }
 class Test
 {
 static void Main(string[] args)
 {
 BookStore<string> store =new BookStore<string>();
 store.Add(“语文”);
 store.Add(“数学”);
 store.Add(“英语”);
 Console.WriteLine(“Store包含以下图书:”);
 foreach (string sin store)
 {
 Console.WriteLine(s);
 }
 Console.ReadKey();
 }
 }
}

Leave a Comment.