泛型类型 是一个编程元素,可适应多个数据类型执行相同功能。 定义泛型类或过程时,无需为每个数据类型定义单独的版本,您可能希望为其执行该功能。
类比是一个带有可拆卸头的螺丝刀套装。 查看螺丝并选择适合该螺丝的正确螺丝刀头(槽型、十字型、星型)。 在螺丝刀手柄中插入正确的头后,使用螺丝刀执行完全相同的功能,即旋转螺丝。
定义泛型类型时,可以使用一个或多个数据类型对其进行参数化。 类型参数允许代码根据其要求定制数据类型。 代码可以声明泛型元素中的多个不同的编程元素,每个元素针对不同的数据类型集进行作。 但声明的元素都执行相同的逻辑,无论它们使用哪种数据类型。
例如,你可能想要创建和使用对特定数据类型(如 String
)进行操作的队列类。 您可以从 System.Collections.Generic.Queue<T>声明这样一个类,正如下述示例所示。
Public stringQ As New System.Collections.Generic.Queue(Of String)
现在,可以使用 stringQ
来专门处理 String
值。 由于 stringQ
专用于 String
,而不是用于 Object
的通用值,因此不需要后期绑定或类型转换。 泛型类型可节省执行时间并减少运行时错误。
有关使用泛型类型的详细信息,请参阅 如何:使用泛型类。
泛型类的示例
下面的示例演示泛型类的框架定义。
Public Class classHolder(Of t)
Public Sub processNewItem(ByVal newItem As t)
Dim tempItem As t
' Insert code that processes an item of data type t.
End Sub
End Class
在前面的框架中,t
是 类型参数,即声明类时提供的数据类型的占位符。 在代码的其他地方,可以通过为 t
提供各种数据类型来声明各种版本的 classHolder
。 下面的示例演示了两个此类声明。
Public integerClass As New classHolder(Of Integer)
Friend stringClass As New classHolder(Of String)
上面的语句声明了 构造类,在这些类中,特定的类型替换了类型形参。 此替换在整个构造类中的代码中传播。 以下示例演示 integerClass
中 processNewItem
过程的外观。
Public Sub processNewItem(ByVal newItem As Integer)
Dim tempItem As Integer
' Inserted code now processes an Integer item.
End Sub
有关更完整的示例,请参阅 如何:定义可以在不同数据类型上提供相同功能的类。
符合条件的编程元素
可以定义和使用泛型类、结构、接口、过程和委托。 .NET 定义多个泛型类、结构和接口,这些类表示常用的泛型元素。 System.Collections.Generic 命名空间提供字典、列表、队列和堆栈。 在定义自己的泛型元素之前,请查看它是否已在 System.Collections.Generic中使用。
过程不是类型,但可以定义和使用泛型过程。 请参阅 Visual Basic 中的泛型过程。
泛型类型的优点
泛型类型是声明多个不同编程元素的基础,每个元素都针对特定数据类型进行作。 泛型类型的替代方法是:
- 对
Object
数据类型进行处理的单一类型。 - 一组类型特定的类型版本。 每个版本分别为一种特定的数据类型(如
String
、Integer
)或用户定义的类型(如customer
)进行编码和操作。
泛型类型在这些替代项上具有以下优势:
- 类型安全。 泛型类型强制进行编译时类型检查。 基于
Object
的类型接受任何数据类型,必须编写代码来检查输入数据类型是否可接受。 使用泛型类型,编译器可以在运行时之前捕获类型不匹配。 - 性能。 泛型类型无需对数据进行装箱和取消装箱操作,原因是每种泛型类型均专用于一种数据类型。 而基于
Object
的操作必须将输入数据类型进行装箱,以将它们转换为Object
,而且还将对预定输出的数据进行取消装箱操作。 装箱和取消装箱操作会降低性能。 此外,还要对基于Object
的类型进行晚期绑定,这意味着需要编写额外的代码才能在运行时访问它们的成员。 类型转换也会降低性能。 - 代码合并。 泛型类型中的代码必须仅定义一次。 一组特定于类型的类型版本必须在每个版本中复制相同的代码,唯一的区别在于该版本的特定数据类型。 使用泛型类型时,特定于类型的版本都从原始泛型类型生成。
- 代码重用。 不依赖于特定数据类型的代码可以在泛型数据类型时重复使用各种数据类型。 即使你最初没有预测的数据类型,也可以重复使用它。
- IDE 支持。 使用从泛型类型声明的构造类型时,集成开发环境(IDE)可以在开发代码时提供更多支持。 例如,IntelliSense 可以向你显示构造函数或方法的参数的特定于类型的选项。
- 泛型算法。 与类型无关的抽象算法非常适合泛型类型。 例如,使用 IComparable 接口对项进行排序的泛型过程可与实现 IComparable的任何数据类型一起使用。
约束
尽管泛型类型定义中的代码应尽可能独立于类型,但可能需要提供泛型类型的任何数据类型的一定功能。 例如,如果要比较两个项目进行排序或整理,其数据类型必须实现 IComparable 接口。 可以通过向类型参数添加 约束 来强制实施此要求。
约束示例
以下示例演示一个具有约束的类的框架定义,该约束要求类型参数实现 IComparable。
Public Class itemManager(Of t As IComparable)
' Insert code that defines class members.
End Class
如果后续代码尝试用一个没有实现 IComparable的类型来从 itemManager
构造类,编译器将会报错。
约束类型
约束可以在任意组合中指定以下要求:
- 类型参数必须实现一个或多个接口
- 类型实参至多只能是一个类的类型,或至多只能从一个类继承
- 类型参数必须公开可从其创建对象的代码可访问的无参数构造函数
- 类型参数必须是 引用类型,或者必须是 值类型
C# 代码可以声明类型参数必须是 非托管类型。 Visual Basic 对 Visual Basic 代码强制实施此约束,该代码使用使用此约束定义的泛型类型或方法(在 C# 中)。 但是,不能对 Visual Basic 中的类型参数声明 unmanaged
约束。
如果需要强制实施多个要求,则可以使用以逗号分隔的 约束列表 (括在大括号 ({ }
) 内)。 若要要求可访问的构造函数,请在列表中包括 New Operator 关键字。 若要要求引用类型,请包含 Class
关键字;若要要求值类型,请包含 Structure
关键字。
有关约束的详细信息,请参阅 类型列表。
多个约束的示例
以下示例显示了泛型类的框架定义,该类具有类型参数的约束列表。 在创建此类实例的代码中,类型参数必须同时实现 IComparable 和 IDisposable 接口,是引用类型,并公开可访问的无参数构造函数。
Public Class thisClass(Of t As {IComparable, IDisposable, Class, New})
' Insert code that defines class members.
End Class
重要术语
泛型类型介绍和使用以下术语:
- 泛型类型。 类、结构、接口、过程或委托的定义,在声明它们时要为它们提供至少一种数据类型。
- 类型形参。 在泛型类型定义中,你在声明数据类型时为其提供的占位符。
- 类型实参。 一种特定的数据类型,用于在你通过泛型类型声明构造类型时替换类型形参。
- 约束。 关于类型形参的条件,用于限制可以为类型形参提供的类型实参。 约束可以要求类型参数实现特定接口、继承自特定类、具有可访问的无参数构造函数或引用类型或值类型。 可以合并这些约束,但最多可以指定一个基类。
- 构造类型。 通过为泛型类型的类型形参提供类型实参,从泛型类型声明的类、结构、接口、过程或委托。