C#泛型C#编程指南CSDN学习整理笔记.docx
- 文档编号:15739240
- 上传时间:2023-07-07
- 格式:DOCX
- 页数:40
- 大小:34.14KB
C#泛型C#编程指南CSDN学习整理笔记.docx
《C#泛型C#编程指南CSDN学习整理笔记.docx》由会员分享,可在线阅读,更多相关《C#泛型C#编程指南CSDN学习整理笔记.docx(40页珍藏版)》请在冰点文库上搜索。
C#泛型C#编程指南CSDN学习整理笔记
1.1、泛型概述
2.0版C#语言和公共语言运行时(CLR)中增加了泛型。
泛型将类型参数的概念引入.NETFramework,类型参数使得设计如下类和方法成为可能:
这些类和方法将一个或多个类型的指定推迟到客户端代码声明并实例化该类或方法的时候。
例如,通过使用泛型类型参数T,您可以编写其他客户端代码能够使用的单个类,而不致引入运行时强制转换或装箱操作的成本或风险,如下所示:
//Declarethegenericclass.
publicclassGenericList
{
VoidAdd(Tinput){}
}
ClassTestGenericList
{
PrivateclassExampleClass{}
StaticvoidMain()
{
//Declarealistoftypeint.
GenericList
//Declarealistoftypestring.
GenericList
//DeclarealistoftypeExampleClass.
GenericList
}
}
泛型概述
∙使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。
∙泛型最常见的用途是创建集合类。
∙.NETFramework类库在System.Collections.Generic命名空间中包含几个新的泛型集合类。
应尽可能地使用这些类来代替普通的类,如System.Collections命名空间中的ArrayList。
∙您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
∙可以对泛型类进行约束以访问特定数据类型的方法。
∙关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
1.2、泛型的优点
在公共语言运行时和C#语言的早期版本中,通用化是通过在类型与通用基类型Object之间进行强制转换来实现的,泛型提供了针对这种限制的解决方案。
通过创建泛型类,您可以创建一个在编译时类型安全的集合。
使用非泛型集合类的限制可以通过编写一小段程序来演示,该程序使用.NETFramework类库中的ArrayList集合类。
ArrayList是一个使用起来非常方便的集合类,无需进行修改即可用来存储任何引用或值类型。
//The.NETFramework1.1waytocreatealist:
System.Collections.ArrayListlist1=newSystem.Collections.ArrayList();
list1.Add(3);
list1.Add(105);
System.Collections.ArrayListlist2=newSystem.Collections.ArrayList();
list2.Add("ItisraininginRedmond.");
list2.Add("Itissnowinginthemountains.");
但这种方便是需要付出代价的。
添加到ArrayList中的任何引用或值类型都将隐式地向上强制转换为Object。
如果项是值类型,则必须在将其添加到列表中时进行装箱操作,在检索时进行取消装箱操作。
强制转换以及装箱和取消装箱操作都会降低性能;在必须对大型集合进行循环访问的情况下,装箱和取消装箱的影响非常明显。
(附:
装箱是将值类型转换为object类型或由此值类型实现的任一接口类型的过程。
当CLR对值类型进行装箱时,会将该值包装到System.Object内部,再将后者存储在托管堆上。
取消装箱将从对象中提取值类型。
装箱是隐式的;取消装箱是显式的。
装箱和取消装箱的概念是类型系统C#统一视图的基础,其中任一类型的值都被视为一个对象。
相对于简单的赋值而言,装箱和取消装箱过程需要进行大量的计算。
对值类型进行装箱时,必须分配并构造一个新对象。
次之,取消装箱所需的强制转换也需要进行大量的计算。
)
另一个限制是缺少编译时类型检查;因为ArrayList会将所有项都强制转换为Object,所以在编译时无法防止客户端代码执行类似如下的操作:
System.Collections.ArrayListlist=newSystem.Collections.ArrayList();
//Addanintegertothelist.
list.Add(3);
//Addastringtothelist.Thiswillcompile,butmaycauseanerrorlater.
list.Add("ItisraininginRedmond.");
intt=0;
//ThiscausesanInvalidCastExceptiontobereturned.
foreach(intxinlist)
{
t+=x;
}
尽管将字符串和ints组合在一个ArrayList中的做法在创建异类集合时是完全可接受的,并且有时需要有意为之,但这种做法很可能产生编程错误,并且直到运行时才能检测到此错误。
在C#语言的1.0和1.1版本中,只能通过编写自己的特定于类型的集合来避免.NETFramework基类库集合类中的通用代码的危险。
当然,由于此类不可对多个数据类型重用,因此将丧失通用化的优点,并且您必须对要存储的每个类型重新编写该类。
ArrayList和其他相似类真正需要的是:
客户端代码基于每个实例指定这些类要使用的具体数据类型的方式。
这样将不再需要向上强制转换为T:
System.Object,同时,也使得编译器可以进行类型检查。
换句话说,ArrayList需要一个类型参数。
这正是泛型所能提供的。
在N:
System.Collections.Generic命名空间的泛型List
//The.NETFramework2.0waytocreatealist
List
//Noboxing,nocasting:
list1.Add(3);
//Compile-timeerror:
//list1.Add("ItisraininginRedmond.");
对于客户端代码,与ArrayList相比,使用List
虽然这种方式稍微增加了编码的复杂性,但好处是您可以创建一个比ArrayList更安全并且速度更快的列表,对于列表项是值类型的情况尤为如此。
1.3、泛型类型参数
在泛型类型或方法定义中,类型参数是客户端在实例化泛型类型的变量时指定的特定类型的占位符。
泛型类(如泛型介绍(C#编程指南)中列出的GenericList
若要使用GenericList
此特定类的类型参数可以是编译器识别的任何类型。
可以创建任意数目的构造类型实例,每个实例使用不同的类型参数,如下所示:
GenericList
GenericList
GenericList
在每个GenericList
通过这种替换方式,我们使用一个类定义创建了三个独立的类型安全的有效对象。
有关CLR如何执行此替换的更多信息,请参见运行时中的泛型(C#编程指南)。
类型参数命名准则
∙务必使用描述性名称命名泛型类型参数,除非单个字母名称完全可以让人了解它表示的含义,而描述性名称不会有更多的意义。
publicinterfaceISessionChannel
publicdelegateTOutputConverter
publicclassList
∙考虑使用T作为具有单个字母类型参数的类型的类型参数名。
publicintIComparer
publicdelegateboolPredicate
publicstructNullable
struct{/*...*/}
∙务必将“T”作为描述性类型参数名的前缀。
publicinterfaceISessionChannel
{
TSessionSession{get;}
}
∙考虑在参数名中指示对此类型参数的约束。
例如,可以将带有ISession约束的参数命名为TSession。
1.4、类型参数的约束
在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。
如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。
这些限制称为约束。
约束是使用where上下文关键字指定的。
下表列出了六种类型的约束:
约束
说明
T:
结构
类型参数必须是值类型。
可以指定除Nullable以外的任何值类型。
有关更多信息,请参见使用可以为null的类型(C#编程指南)。
T:
类
类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。
T:
new()
类型参数必须具有无参数的公共构造函数。
当与其他约束一起使用时,new()约束必须最后指定。
T:
<基类名>
类型参数必须是指定的基类或派生自指定的基类。
T:
<接口名称>
类型参数必须是指定的接口或实现指定的接口。
可以指定多个接口约束。
约束接口也可以是泛型的。
T:
U
为T提供的类型参数必须是为U提供的参数或派生自为U提供的参数。
使用约束的原因
如果要检查泛型列表中的某个项以确定它是否有效,或者将它与其他某个项进行比较,则编译器必须在一定程度上保证它需要调用的运算符或方法将受到客户端代码可能指定的任何类型参数的支持。
这种保证是通过对泛型类定义应用一个或多个约束获得的。
例如,基类约束告诉编译器:
仅此类型的对象或从此类型派生的对象才可用作类型参数。
一旦编译器有了这个保证,它就能够允许在泛型类中调用该类型的方法。
约束是使用上下文关键字where应用的。
下面的代码示例演示可通过应用基类约束添加到GenericList
publicclassEmployee
{
privatestringname;
privateintid;
publicEmployee(strings,inti)
{
name=s;
id=i;
}
publicstringName
{
get{returnname;}
set{name=value;}
}
publicintID
{
get{returnid;}
set{id=value;}
}
}
publicclassGenericList
Employee
{
privateclassNode
{
privateNodenext;
privateTdata;
publicNode(Tt)
{
next=null;
data=t;
}
publicNodeNext
{
get{returnnext;}
set{next=value;}
}
publicTData
{
get{returndata;}
set{data=value;}
}
}
privateNodehead;
publicGenericList()//constructor
{
head=null;
}
publicvoidAddHead(Tt)
{
Noden=newNode(t);
n.Next=head;
head=n;
}
publicIEnumerator
{
Nodecurrent=head;
while(current!
=null)
{
yieldreturncurrent.Data;
current=current.Next;
}
}
publicTFindFirstOccurrence(strings)
{
Nodecurrent=head;
Tt=null;
while(current!
=null)
{
//TheconstraintenablesaccesstotheNameproperty.
if(current.Data.Name==s)
{
t=current.Data;
break;
}
else
{
current=current.Next;
}
}
returnt;
}
}
约束使得泛型类能够使用Employee.Name属性,因为类型为T的所有项都保证是Employee对象或从Employee继承的对象。
可以对同一类型参数应用多个约束,并且约束自身可以是泛型类型,如下所示:
classEmployeeList
Employee,IEmployee,System.IComparable
{
//...
}
通过约束类型参数,可以增加约束类型及其继承层次结构中的所有类型所支持的允许操作和方法调用的数量。
因此,在设计泛型类或方法时,如果要对泛型成员执行除简单赋值之外的任何操作或调用System.Object不支持的任何方法,您将需要对该类型参数应用约束。
在应用whereT:
class约束时,避免对类型参数使用==和!
=运算符,因为这些运算符仅测试引用同一性而不测试值相等性。
即使在用作参数的类型中重载这些运算符也是如此。
下面的代码说明了这一点;即使String类重载==运算符,输出也为false。
publicstaticvoidOpTest
class
{
System.Console.WriteLine(s==t);
}
staticvoidMain()
{
strings1="target";
System.Text.StringBuildersb=newSystem.Text.StringBuilder("target");
strings2=sb.ToString();
OpTest
}
这种情况的原因在于,编译器在编译时仅知道T是引用类型,因此必须使用对所有引用类型都有效的默认运算符。
如果必须测试值相等性,建议的方法是同时应用whereT:
IComparable
约束多个参数
可以对多个参数应用约束,并对一个参数应用多个约束,如下面的示例所示:
classBase{}
classTest
whereU:
struct
whereT:
Base,new(){}
未绑定的类型参数
没有约束的类型参数(如公共类SampleClass
未绑定的类型参数具有以下规则:
∙不能使用!
=和==运算符,因为无法保证具体类型参数能支持这些运算符。
∙可以在它们与System.Object之间来回转换,或将它们显式转换为任何接口类型。
∙可以将它们与null进行比较。
将未绑定的参数与null进行比较时,如果类型参数为值类型,则该比较将始终返回false。
作为约束的类型参数
将泛型类型参数作为约束使用,在具有自己类型参数的成员函数必须将该参数约束为包含类型的类型参数时非常有用,如下示例所示:
classList
{
voidAdd(Listitems)whereU:
T{/*...*/}
}
在上面的示例中,T在Add方法的上下文中是一个类型约束,而在List类的上下文中是一个未绑定的类型参数。
类型参数还可在泛型类定义中用作约束。
请注意,必须在尖括号中声明此类型参数与任何其他类型的参数:
//TypeparameterVisusedasatypeconstraint.
publicclassSampleClass
V{}
泛型类的类型参数约束的作用非常有限,因为编译器除了假设类型参数派生自System.Object以外,不会做其他任何假设。
在希望强制两个类型参数之间的继承关系的情况下,可对泛型类使用参数类型约束。
1.5、泛型类
泛型类封装不是特定于具体数据类型的操作。
泛型类最常用于集合,如链接列表、哈希表、堆栈、队列、树等。
像从集合中添加和移除项这样的操作都以大体上相同的方式执行,与所存储数据的类型无关。
对于大多数需要集合类的方案,推荐的方法是使用.NETFramework类库中所提供的类。
有关使用这些类的更多信息,请参见.NETFramework类库中的泛型(C#编程指南)。
一般情况下,创建泛型类的过程为:
从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。
创建您自己的泛型类时,需要特别注意以下事项:
∙将哪些类型通用化为类型参数。
通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。
但是,太多的通用化会使其他开发人员难以阅读或理解代码。
∙如果存在约束,应对类型参数应用什么约束(请参见类型参数的约束(C#编程指南))。
一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。
例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。
这可以防止您的类被意外地用于值类型,并允许您对T使用as运算符以及检查空值。
∙是否将泛型行为分解为基类和子类。
由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。
请参见本主题后面有关从泛型基类继承的规则。
∙是否实现一个或多个泛型接口。
例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如IComparable
有关简单泛型类的示例,请参见泛型介绍(C#编程指南)。
类型参数和约束的规则对于泛型类行为有几方面的含义,特别是关于继承和成员可访问性。
您应当先理解一些术语,然后再继续进行。
对于泛型类Node<T>,客户端代码可通过指定类型参数来引用该类,以便创建封闭式构造类型(Node
或者可以让类型参数处于未指定状态(例如在指定泛型基类时)以创建开放式构造类型(Node
泛型类可以从具体的、封闭式构造或开放式构造基类继承:
classBaseNode{}
classBaseNodeGeneric
//concretetype
classNodeConcrete
BaseNode{}
//closedconstructedtype
classNodeClosed
BaseNodeGeneric
//openconstructedtype
classNodeOpen
BaseNodeGeneric
非泛型类(换句话说,即具体类)可以从封闭式构造基类继承,但无法从开放式构造类或类型参数继承,因为在运行时客户端代码无法提供实例化基类所需的类型参数。
//Noerror
classNode1:
BaseNodeGeneric
//Generatesanerror
//classNode2:
BaseNodeGeneric
//Generatesanerror
//classNode3:
T{}
从开放式构造类型继承的泛型类必须为任何未被继承类共享的基类类型参数提供类型变量,如以下代码所示:
classBaseNodeMultiple
//Noerror
classNode4
BaseNodeMultiple
//Noerror
classNode5
BaseNodeMultiple
//Generatesanerror
//classNode6
BaseNodeMultiple
从开放式构造类型继承的泛型类必须指定约束,这些约束是基类型约束的超集或暗示基类型约束:
classNodeItem
System.IComparable
classSpecialNodeItem
NodeItem
System.IComparable
泛型类型可以使用多个类型参数和约束,如下所示:
classSuperKeyType
whereU:
System.IComparable
whereV:
new()
{}
开放式构造类型和封闭式构造类型可以用作方法参数:
voi
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 泛型 编程 指南 CSDN 学习 整理 笔记