轻松学C#之泛型

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第16章泛型泛型是C#2.0的一个新增加的特性,是C#编程中不可缺少的部分。它是程序设计语言的一种特性,为使用C#语言编写面向对象程序增加了极大的有效性和灵活性。它把指定类型的工作推迟到客户端代码声明并实例化类或方法的时候进行。泛型的功能非常重要,因为通过使用泛型创建的类、结构、接口、方法和委托能过以一种类型安全的方式操作各种数据。许多算法在逻辑上是相同的。本章将详细介绍泛型的语法、理论以及用法。16.1泛型简介我们在编写程序时,经常会遇到两个模块功能非常相似,但方法的参数类型不同的情况。例如下面的例子:usingSystem;classMyClass{publicvoidF(inti){Console.WriteLine(i+是int类型。);}staticvoidMain(string[]args){MyClassmyclass=newMyClass();myclass.F(10);Console.ReadLine();}}在本例中,我们在MyClass类中写了一个参数为int型的方法F(),其作用是输出i是int类型。上面的代码运行地很好,但是当我们需要一个参数为string类型的方法时,该怎么做?很多人会想到把方法F()复制一份,将参数类型由int改为string就可以了。这样做本身是没有问题的,但是如果我们还需要long、double类型作为参数时该怎么做呢?还是复制吗?有没有办法在方法中传入通用的数据类型,这样不就可以合并代码了吗?泛型的出现就解决了这个问题。泛型使用一个通用的数据类型T来代替object,在类实例化时指定T的类型,运行时自动编译为本地代码,运行效率和代码质量都有很大提高。声明泛型类的语法如下:classclass-nametype-param-list{}声明一个对泛型类的引用的语法如下:class-nametype-arg-listvar-name=newclass-nametype-arg-list(cons-arg-list);下面的泛型用来重写上面的例子,用一个通用的数据类型T来作为一个占位符,等待在实例化时用一个实际的类型来代替。usingSystem;classTestT{Tobj;publicvoidF(Tt){obj=t;Console.WriteLine(泛型类Test的一个参数的方法F():+t.ToString());}publicvoidF(){Console.WriteLine(泛型类Test的无参数方法F()。);}}classProgram{staticvoidMain(string[]args){TestProgramt1=newTestProgram();Teststringt2=newTeststring();Testintt3=newTestint();t1.F();t2.F(Hello!);t3.F(10);Console.ReadLine();}}16.2类型约束可以使用任意类型替换类型形参。例如:classGenT{}可以为T指定任意类型。因此,在创建Gen对象时使用int、double、string、FileStream或者任意其他类型替换T都是合法的。尽管对类型实参不加限制在大多数情况下都适用,但是在某些情况下,限制可以用作类型实参的类型也是必要的。例如,不允许使用int类型作为类型实参。为了处理某些情况,C#提供了类型约束。在指定一个类型形参是,可以指定类型形参必须满足的约束条件。通过在指定类型形参时使用where子句来实现指定。C#定义了如下类型的约束:基类约束接口约束构造函数约束引用类型约束值类型约束16.2.1基类约束基类约束有两个功能。首先,它允许在泛型类中使用自由约束指定的基类所定义的成员。通过提高基类约束,编译器将知道所有的类型实参都拥有由指定的基类所定义的成员。基类约束的第二个功能是,确保只使用支持指定基类的类型实参。这意味着对于任意给定的基类约束,类型实参必须是基类本身或者是派生于该基类的类。基类约束使用如下形式的where子句:whereT:base-class-name其中,T是类型形参的名称,base-class-name是基类的名称。只能指定一个基类。【示例16-1】演示基类约束的用法。16.2.2接口约束接口约束是指定某个类型实参必须实现的接口。它的两个主要功能与基类约束一样,允许开发人员在泛型类中使用接口的成员;确保只能使用实现了特定接口的类型实参。也就是说对任何给定的接口约束,类型实参必须是接口本身或者实现了该接口的类。接口约束使用的where子句具有以下形式:whereT:interface-name其中,T是类型形参的名称,interface-name是接口的名称。可以使用逗号分隔开指定的多个接口。若某个约束同时包含基类和接口,则需先指定基类列表,再指定接口列表。【示例16-2】下面的程序用来说明接口约束的用途。在本例中,Test类指定的接口约束要求类型实参实现MyClass1接口。因为MyClass2类实现了MyClass1接口,所有它是绑定给T的有效类型。倘若定义一个MyClass3类,没有实现MyClass1的接口,因此不可以绑定给T。此时,程序不能通过编译。16.2.3NEW()构造函数约束new()构造函数约束允许开发人员实例化一个泛型类型的对象。new()约束要求类型实参必须提供一个无参数的公有构造函数。使用new()约束时,可以通过调用该无参数的构造函数来创建对象。new()构造函数约束的形式为:whereT:new()使用new()约束时应当注意3点:1.new()约束可以与其他约束一起使用,但必须位于约束列表的末端。2.new()约束仅允许开发人员使用无参数的构造函数构造一个对象,即使同时存在其他的构造函数也是如此。即不允许给类型形参的构造函数传递实参。3.不可以同时使用new()约束和值类型约束。【示例16-3】下面通过一个简单是示例来说明new()构造函数约束的用法。16.2.4引用类型约束和值类型约束如果引用类型和值类型之间的差别对于泛型代码非常重要,引用类型约束和值类型约束就很有用。当同时存在其他约束时,引用类型约束或值类型约束必须位于约束列表的开头。1.引用类型约束将一个类型形参限定为引用类型。引用类型一般是用户定义的类型,包含类、接口、委托、字符串和数组类型。引用类型约束使用class关键字,它的通用形式为whereT:class在这个where子句中,class关键字是指定T必须是引用类型。因此,尝试对T使用值类型,将会导致编译错误。【示例16-4】下面的代码展示如何实现引用类型约束。在本例中,泛型类Test中的第二个class关键字是指定泛型类型的参数T必须是引用类型。而且,还可以指定一个逗号分隔符的接口约束列表,来指定该类型必须实现的接口,比如示例中的接口MyClass1。2.值类型约束将一个类型形参限定为值类型。值类型派生于System.ValueType类型。基元和结构都是值类型。值类型约束使用struct关键字,它的通用形式为:whereT:struct在该形式中,struct关键字指定T必须是值类型。因此,尝试对T使用引用类型,将导致编译错误。【示例16-5】下面的代码展示如何实现值类型约束。在本例中,泛型类Test中的struct关键字是指定泛型类型的参数T必须是值类型。Testint和TestMyClass都是有效的,而TestProgram不是有效的。要验证这点,可以删除类Program中的注释符号,然后重新编译程序,此时将产生错误。16.2.5使用约束建立两个类型形参之间的关系基类约束有一个变体形式,它允许开发人员在两个类型形参之间建立一种关系。仔细观察如下声明:classTestT,VwhereV:T{}在该声明中,where子句告诉编译器绑定给V的类型实参必须等同于或者继承于传递给T的类型实参。如果在声明Test类型的对象时这种关系不存在,那么将导致编译错误。如上面所示,使用这种类型形参的约束称为“裸类型约束”。【示例16-6】下面的程序演示了这种约束。在本例中,MyClass2类继承了MyClass1类,所以TestMyClass1,MyClass2t=newTestMyClass1,MyClass2();是合法的。倘若将MyClass1和MyClass2的位置调换,就会出错,因为MyClass1类没有继承MyClass2类。16.2.6使用多个约束同一个类型形参可以使用多个约束。这种情况下,需要使用一个由逗号分隔的约束列表。在该列表中,第一个约束必须是引用类型约束或者值类型约束,或者是基类约束。指定引用类型约束或值类型约束的同时也指定基类约束是非法的。接下来必须是所有的接口约束,最后是new()约束。下面是一个合法的声明:classTestTwhereT:MyClass,Iinterface,new(){}在上述声明中,用于替换T的类型实参必须继承MyClass类,实现Iinterface接口,并且拥有一个无参数的构造函数。在使用两个或者更多的类型形参时,可以使用多条where子句分别为它们指定约束。【示例16-7】下面的程序演示了这种约束。16.3泛型结构C#允许创建泛型结构。它们非常类似于泛型类,只是没有继承特性。其语法与创建泛型类完全一样。泛型结构的声明方式为:structAT{}【示例16-8】下面的程序演示了泛型结构的用法。在本例中,声明了一个泛型结构A。与泛型类相似,泛型结构也可以使用约束。如下所示为将泛型结构A声明的类型实参限制为值类型:structATwhereT:struct{}16.4泛型方法泛型方法是声明了类型参数的方法。泛型方法可以在类、结构或接口声明中声明,这些类、结构、或接口本身可以是泛型或非泛型。泛型类内部的方法可以使用类的类型参数,自动创建与该类型形参相关的泛型。也可以声明使用一个或多个自定义类型形参的泛型方法。泛型方法可以是静态的,也可以是非静态的。泛型方法的声明形式如下:publicvoidFT{}【示例16-9】下面程序演示了泛型方法的用法。在上面的例子中,声明了一个名为A的非泛型类,在该类中有一个静态的泛型方法F()。类型形参的声明紧跟在方法名称之后,但位于参数列表之前。F()方法是静态的,因此与其调用与具体的对象无关。16.5泛型委托与方法一样,委托也可以是泛型的。因为泛型内在的类型安全性,无法将不兼容的方法赋给委托。声明泛型委托的形式为:delegateret-typedelegate-nametype-parameter-list(arg-list);类型形参的声明紧跟在委托的名称之后。泛型委托的优点在于,它允许开发人员以类型安全的方式定义一种通用形式,该形式可用于匹配任意兼容的方法。【示例16-10】下面的程序演示了一个名为Test的泛型委托。它有一个类型形参T,该委托返回类型T,并且接收一个T类型的实参。16.6泛型接口除了定义泛型类和泛型方法外,还可以定义泛型接口。泛型接口的定义与泛型类基本相同。泛型接口的声明形式为:interfaceIinterfaceT{}【示例16-11】下面的程序演示了一个名为Iinterface的泛型接口。16.7泛型类的层次结构泛型类可以是类的层次结构的一部分。所以,泛型类可以用作基类或者派生类。泛型层次结构与非泛型层次结构的主要不同在于,在泛型层次结构中,泛型基类所需的任意类型实参都必须通过派生类沿着层次结构向上传递。泛型类的层次结构有两种:1.使用泛型基类【示例16-12】下面是一个简单的泛型类层次结构例子,它使用了一个泛型基类。在该例中,Test2类继承了Test1泛型类。Test2类指定了类型形参T并将

1 / 21
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功