Visual Basic 中的泛型类型 (Visual Basic)
“泛型类型”是可适应对多种数据类型执行相同功能的单个编程元素。 定义泛型类或过程时,无需为可能需要对其执行该功能的每个数据类型定义单独版本。
就好比是带有可拆卸刀头的螺丝刀。 您检查需要拧动的螺丝,然后选择适合该螺丝的刀头(一字、十字、星形)。 将正确的刀头插入到螺丝刀柄上后,您就可以使用螺丝刀执行完全相同的功能,即拧螺丝。
螺丝刀就是泛型工具
定义泛型类型时,即使用一个或多个数据类型将其参数化。 这样可允许使用代码定制数据类型以满足其要求。 代码可以通过泛型元素声明若干个不同的编程元素,每个元素可使用一组不同的数据类型。 但是,无论声明的元素使用哪些数据类型,它们均执行相同的逻辑。
例如,您可能想创建并使用一个处理特定数据类型(如 String)的队列类。 可以通过 System.Collections.Generic.Queue<T> 声明一个如下例所示的类。
Public stringQ As New System.Collections.Generic.Queue(Of String)
现在,可以使用 stringQ 来专门处理 String 值。 由于 stringQ 专用于 String 而未针对 Object 值进行泛型化,因此,不会有后期绑定或类型转换。 这就节省了执行时间并减少了运行时错误。
有关使用泛型类型的更多信息,请参见 如何:使用泛型类 (Visual Basic)。
泛型类的示例
下面的示例演示泛型类的主干定义。
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)
上面的语句声明了构造类,在这些类中,特定的类型替换了类型参数。 此类替换会传播到构造类中的代码内。 下面的示例显示了 processNewItem 过程在 integerClass 中的形式。
Public Sub processNewItem(ByVal newItem As Integer)
Dim tempItem As Integer
' Inserted code now processes an Integer item.
End Sub
有关更完整的示例,请参见 如何:定义可对不同数据类型提供相同功能的类 (Visual Basic)。
合格的编程元素
可以定义并使用泛型类、结构、接口、过程和委托。 请注意,.NET Framework 定义了几个代表常用泛型元素的泛型类、结构和接口。 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 中构造一个类,则编译器会引发错误。
约束的类型
约束可以按任意组合指定下列要求:
类型实参必须实现一个或多个接口
类型参数至多只能是一个类的类型,或至多只能从一个类继承
对于通过类型参数创建对象的代码,类型参数必须公开一个可供其访问的无参数构造函数
类型参数必须是引用类型或值类型
如果需要强制实施多个要求,则可以使用以逗号分隔的约束列表(括在大括号 ({ }) 中)。 若要求可访问的构造函数,请在此列表中加入 New 运算符 (Visual Basic) 关键字。 若要求引用类型,请加入 Class 关键字;若要求值类型,请加入 Structure 关键字。
有关约束的更多信息,请参见 类型列表 (Visual Basic)。
多个约束的示例
下面的示例演示带有类型参数约束列表的泛型类的主干定义。 在创建此类的实例的代码中,类型参数必须实现 IComparable 和 IDisposable 接口,必须是引用类型,并且必须公开一个可访问的无参数构造函数。
Public Class thisClass(Of t As {IComparable, IDisposable, Class, New})
' Insert code that defines class members.
End Class
重要术语
泛型类型引入并使用了以下术语:
泛型类型。 类、结构、接口、过程或委托的定义,在声明它们时要为它们提供至少一种数据类型。
类型参数。 在泛型类型定义中,您在声明数据类型时为其提供的占位符。
类型变量。 特定的数据类型,用于在您通过泛型类型声明构造类型时替换类型参数。
约束。 有关类型参数的条件,限制了可以为类型参数提供的类型变量。 约束可以要求类型参数必须实现特定的接口,必须是特定的类或继承自特定的类,必须具有可访问的无参数构造函数,或者必须是引用类型或值类型。 可以组合这些约束,但至多只能指定一个类。
构造类型。 通过为泛型类型的类型参数提供类型变量,从泛型类型中声明的类、结构、接口、过程或委托。