Visual Basic 中的两种基本类型类别是值类型和引用类型。 基元类型(字符串除外)、枚举和结构是值类型。 类、字符串、标准模块、接口、数组和委托是引用类型。
每个类型都有一个 默认值,该值是在初始化时分配给该类型的变量的值。
TypeName
: ArrayTypeName
| NonArrayTypeName
;
NonArrayTypeName
: SimpleTypeName
| NullableTypeName
;
SimpleTypeName
: QualifiedTypeName
| BuiltInTypeName
;
QualifiedTypeName
: Identifier TypeArguments? (Period IdentifierOrKeyword TypeArguments?)*
| 'Global' Period IdentifierOrKeyword TypeArguments?
(Period IdentifierOrKeyword TypeArguments?)*
;
TypeArguments
: OpenParenthesis 'Of' TypeArgumentList CloseParenthesis
;
TypeArgumentList
: TypeName ( Comma TypeName )*
;
BuiltInTypeName
: 'Object'
| PrimitiveTypeName
;
TypeModifier
: AccessModifier
| 'Shadows'
;
IdentifierModifiers
: NullableNameModifier? ArrayNameModifier?
;
值类型和引用类型
尽管值类型和引用类型在声明语法和用法方面可能相似,但它们的语义是不同的。
引用类型存储在运行时堆中;只能通过对该存储的引用来访问它们。 由于引用类型始终通过引用访问,因此其生存期由 .NET Framework 管理。 跟踪对特定实例的未完成引用,并且仅当不再保留任何引用时,才会销毁实例。 引用类型的变量包含对该类型的值的引用、更派生类型的值或 null 值的引用。 null 值不引用任何值;除了分配 null 值之外,无法执行任何作。 对引用类型的变量的赋值将创建引用的副本,而不是引用值的副本。 对于引用类型的变量,默认值为 null 值。
值类型直接存储在堆栈中,无论是在数组中还是在另一种类型中;只能直接访问其存储。 由于值类型直接存储在变量中,因此它们的生存期由包含它们的变量的生存期确定。 当包含值类型实例的位置被销毁时,值类型实例也会被销毁。 始终直接访问值类型;无法创建对值类型的引用。 禁止此类引用使得无法引用已销毁的值类实例。 由于值类型始终 NotInheritable是,因此值类型的变量始终包含该类型的值。 因此,值类型的值不能为 null 值,也不能引用更派生类型的对象。 对值类型的变量的赋值将创建要分配的值的副本。 对于值类型的变量,默认值是将类型的每个变量成员初始化为其默认值的结果。
以下示例显示了引用类型和值类型之间的差异:
Class Class1
Public Value As Integer = 0
End Class
Module Test
Sub Main()
Dim val1 As Integer = 0
Dim val2 As Integer = val1
val2 = 123
Dim ref1 As Class1 = New Class1()
Dim ref2 As Class1 = ref1
ref2.Value = 123
Console.WriteLine("Values: " & val1 & ", " & val2)
Console.WriteLine("Refs: " & ref1.Value & ", " & ref2.Value)
End Sub
End Module
程序的输出为:
Values: 0, 123
Refs: 123, 123
对局部变量 val2 的赋值不会影响局部变量 val1 ,因为两个局部变量都是值类型(类型 Integer),并且值类型的每个局部变量都有自己的存储。 相比之下,赋值 ref2.Value = 123; 会影响这两个对象 ref1 和 ref2 引用的对象。
需要注意的一点是,即使结构、枚举和基元类型(除外 String)都是值类型,但它们都继承自引用类型。 结构和基元类型继承自引用类型,该引用类型继承自该引用Object类型System.ValueType。 枚举类型继承自引用类型,该引用类型继承自该引用System.ValueType类型System.Enum。
可以为 null 的值类型
对于值类型, ? 可以将修饰符添加到类型名称中,以表示该类型的 可为 null 版本。
NullableTypeName
: NonArrayTypeName '?'
;
NullableNameModifier
: '?'
;
可以为 null 的值类型可以包含与该类型不可为 null 版本以及 null 值相同的值。 因此,对于可以为 null 的值类型,分配给 Nothing 类型的变量会将变量的值设置为 null 值,而不是值类型的零值。 例如:
Dim x As Integer = Nothing
Dim y As Integer? = Nothing
' Prints zero
Console.WriteLine(x)
' Prints nothing (because the value of y is the null value)
Console.WriteLine(y)
还可以通过将可为 null 的类型修饰符放在变量名称上,将变量声明为可以为 null 的值类型。 为清楚起见,对变量名称和同一声明中的类型名称具有可为 null 的类型修饰符无效。 由于可以使用类型实现可为 null 的类型 System.Nullable(Of T),因此该类型 T? 是该类型的 System.Nullable(Of T)同义词,并且两个名称可以互换使用。 不能将 ? 修饰符放置在已经可以为 null 的类型上;因此,无法声明该类型 Integer?? 或 System.Nullable(Of Integer)?。
可以为 null 的值类型具有从基础类型T?提升到该类型TT?的任何运算符或转换的成员System.Nullable(Of T)。 从基础类型中解除复制运算符和转换,在大多数情况下,将可为 null 的值类型替换为不可为 null 的值类型。 这允许许多同样适用于 T 应用的 T? 转换和作。
接口实现
结构和类声明可以声明它们通过一个或多个 Implements 子句实现一组接口类型。
TypeImplementsClause
: 'Implements' TypeImplements StatementTerminator
;
TypeImplements
: NonArrayTypeName ( Comma NonArrayTypeName )*
;
子句中指定的 Implements 所有类型必须是接口,并且该类型必须实现接口的所有成员。 例如:
Interface ICloneable
Function Clone() As Object
End Interface
Interface IComparable
Function CompareTo(other As Object) As Integer
End Interface
Structure ListEntry
Implements ICloneable, IComparable
...
Public Function Clone() As Object Implements ICloneable.Clone
...
End Function
Public Function CompareTo(other As Object) As Integer _
Implements IComparable.CompareTo
...
End Function
End Structure
实现接口的类型还隐式实现接口的所有基接口。 即使类型未显式列出子句中的所有 Implements 基接口,也是如此。 在此示例中,结构 TextBox 实现这两个和 IControlITextBox。
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Structure TextBox
Implements ITextBox
...
Public Sub Paint() Implements ITextBox.Paint
...
End Sub
Public Sub SetText(text As String) Implements ITextBox.SetText
...
End Sub
End Structure
声明类型实现接口本身不会在类型的声明空间中声明任何内容。 因此,使用同名方法实现两个接口是有效的。
类型本身无法实现类型参数,尽管它可能涉及范围中的类型参数。
Class C1(Of V)
Implements V ' Error, can't implement type parameter directly
Implements IEnumerable(Of V) ' OK, not directly implementing
...
End Class
可以使用不同的类型参数多次实现泛型接口。 但是,如果提供的类型参数(无论类型约束如何)可能与该接口的另一个实现重叠,泛型类型不能使用类型参数实现泛型接口。 例如:
Interface I1(Of T)
End Interface
Class C1
Implements I1(Of Integer)
Implements I1(Of Double) ' OK, no overlap
End Class
Class C2(Of T)
Implements I1(Of Integer)
Implements I1(Of T) ' Error, T could be Integer
End Class
基元类型
基 元类型 通过关键字标识,这些关键字是命名空间中 System 预定义类型的别名。 基元类型与它别名的类型完全不区分:编写保留字 Byte 与写入 System.Byte完全相同。 基元类型也称为 内部类型。
PrimitiveTypeName
: NumericTypeName
| 'Boolean'
| 'Date'
| 'Char'
| 'String'
;
NumericTypeName
: IntegralTypeName
| FloatingPointTypeName
| 'Decimal'
;
IntegralTypeName
: 'Byte' | 'SByte' | 'UShort' | 'Short' | 'UInteger'
| 'Integer' | 'ULong' | 'Long'
;
FloatingPointTypeName
: 'Single' | 'Double'
;
由于基元类型别名为常规类型,因此每个基元类型都有成员。 例如, Integer 在 .. 中 System.Int32声明了成员。 文本可以视为其相应类型的实例。
基元类型不同于其他结构类型,因为它们允许某些附加作:
基元类型允许通过编写文本来创建值。 例如,
123I类型为文本Integer。可以声明基元类型的常量。
当表达式的作数都是基元类型常量时,编译器可以在编译时计算表达式。 此类表达式称为常量表达式。
Visual Basic 定义以下基元类型:
整型值类型
Byte(1 字节无符号整数)、SByte(1 字节有符号整数)、UShort(2 字节无符号整数)、(2 字节无符号整数)、Short(4 字节无符号整数)、UInteger(4 字节无符号整数)、IntegerULong(8 字节无符号整数)和Long(8 字节有符号整数)。 这些类型分别映射到System.Byte、、System.SByte、System.UInt16、System.UInt32System.Int16、System.Int32System.UInt64和System.Int64。 整型类型的默认值等效于文本0。浮点值类型
Single(4 字节浮点)和Double(8 字节浮点)。 这些类型分别映射到System.Single和System.Double映射。 浮点类型的默认值等效于文本0。映射到
Decimal的类型(16 字节十进制值System.Decimal)。 十进制的默认值等效于文本0D。表示
Boolean真实值的值类型,通常是关系或逻辑运算的结果。 文本的类型System.Boolean。 类型的默认值Boolean等效于文本False。值
Date类型,表示日期和/或时间和映射到System.DateTime。 类型的默认值Date等效于文本# 01/01/0001 12:00:00AM #。值
Char类型,表示单个 Unicode 字符并映射到System.Char。 类型的默认值Char等效于常量表达式ChrW(0)。引用
String类型,表示 Unicode 字符序列并映射到System.String。 类型的默认值String为 null 值。
枚举
枚举 是继承 System.Enum 自和以符号方式表示基元整型类型的一组值的值类型。
EnumDeclaration
: Attributes? TypeModifier* 'Enum' Identifier
( 'As' NonArrayTypeName )? StatementTerminator
EnumMemberDeclaration+
'End' 'Enum' StatementTerminator
;
对于枚举类型 E,默认值为表达式 CType(0, E)生成的值。
枚举的基础类型必须是一个整数类型,可以表示枚举中定义的所有枚举器值。 如果指定了基础类型,则它必须是命名空间中的相应类型之一、SByte、UShort、、 IntegerLongUIntegerULongShortSystemByte 如果未显式指定基础类型,则默认值为 Integer。
以下示例声明一个基础类型的 Long枚举:
Enum Color As Long
Red
Green
Blue
End Enum
开发人员可以选择使用基础类型的基础类型Long(如示例中所示),以允许使用区域中的值(但不在范围Integer)或保留此选项供将来使用Long。
枚举成员
枚举的成员是在枚举中声明的枚举值,以及继承自类 System.Enum的成员。
枚举成员的范围是枚举声明正文。 这意味着,在枚举声明之外,枚举成员必须始终限定(除非该类型通过命名空间导入专门导入到命名空间中)。
当省略常量表达式值时,枚举成员声明的声明顺序非常重要。 枚举成员仅具有 Public 访问权限;枚举成员声明上不允许任何访问修饰符。
EnumMemberDeclaration
: Attributes? Identifier ( Equals ConstantExpression )? StatementTerminator
;
枚举值
枚举成员列表中的枚举值声明为类型为基础枚举类型的常量,并且无论需要常量,都可以显示这些常量。 一个枚举成员定义,它 = 为关联的成员提供常量表达式指示的值。 常量表达式必须计算为可隐式转换为基础类型的整型类型,并且必须位于可由基础类型表示的值范围内。 下面的示例出错,因为常量值1.52.3,并且3.3不能隐式转换为具有严格语义的基础整型类型Long。
Option Strict On
Enum Color As Long
Red = 1.5
Green = 2.3
Blue = 3.3
End Enum
多个枚举成员可以共享相同的关联值,如下所示:
Enum Color
Red
Green
Blue
Max = Blue
End Enum
该示例显示具有两个枚举成员的枚举, Blue 以及 Max 具有相同关联值的枚举。
如果枚举中的第一个枚举器值定义没有初始值设定项,则相应的常量的值为 0。 没有初始值设定项的枚举值定义为枚举器提供通过增加上一个枚举值的值来 1获取的值。 此增加的值必须位于可由基础类型表示的值范围内。
Enum Color
Red
Green = 10
Blue
End Enum
Module Test
Sub Main()
Console.WriteLine(StringFromColor(Color.Red))
Console.WriteLine(StringFromColor(Color.Green))
Console.WriteLine(StringFromColor(Color.Blue))
End Sub
Function StringFromColor(c As Color) As String
Select Case c
Case Color.Red
Return String.Format("Red = " & CInt(c))
Case Color.Green
Return String.Format("Green = " & CInt(c))
Case Color.Blue
Return String.Format("Blue = " & CInt(c))
Case Else
Return "Invalid color"
End Select
End Function
End Module
上面的示例输出枚举值及其关联值。 输出为:
Red = 0
Green = 10
Blue = 11
值的原因如下:
枚举值
Red会自动分配值0(因为它没有初始值设定项,并且是第一个枚举值成员)。枚举值
Green显式给定值10。枚举值
Blue自动分配一个大于其前面文本的枚举值的值。
常量表达式不能直接或间接使用其自己的关联枚举值的值(即不允许常量表达式中的循环)。 下面的示例无效, A 因为声明和 B 声明是循环的。
Enum Circular
A = B
B
End Enum
A 依赖于 B 显式,并 B 隐式依赖 A 。
课程
类是可能包含数据成员(常量、变量和事件)、函数成员(方法、属性、索引器、运算符和构造函数)和嵌套类型的数据结构。 类是引用类型。
ClassDeclaration
: Attributes? ClassModifier* 'Class' Identifier TypeParameterList? StatementTerminator
ClassBase?
TypeImplementsClause*
ClassMemberDeclaration*
'End' 'Class' StatementTerminator
;
ClassModifier
: TypeModifier
| 'MustInherit'
| 'NotInheritable'
| 'Partial'
;
以下示例演示包含每种成员的类:
Class AClass
Public Sub New()
Console.WriteLine("Constructor")
End Sub
Public Sub New(value As Integer)
MyVariable = value
Console.WriteLine("Constructor")
End Sub
Public Const MyConst As Integer = 12
Public MyVariable As Integer = 34
Public Sub MyMethod()
Console.WriteLine("MyClass.MyMethod")
End Sub
Public Property MyProperty() As Integer
Get
Return MyVariable
End Get
Set (value As Integer)
MyVariable = value
End Set
End Property
Default Public Property Item(index As Integer) As Integer
Get
Return 0
End Get
Set (value As Integer)
Console.WriteLine("Item(" & index & ") = " & value)
End Set
End Property
Public Event MyEvent()
Friend Class MyNestedClass
End Class
End Class
以下示例演示了这些成员的用法:
Module Test
' Event usage.
Dim WithEvents aInstance As AClass
Sub Main()
' Constructor usage.
Dim a As AClass = New AClass()
Dim b As AClass = New AClass(123)
' Constant usage.
Console.WriteLine("MyConst = " & AClass.MyConst)
' Variable usage.
a.MyVariable += 1
Console.WriteLine("a.MyVariable = " & a.MyVariable)
' Method usage.
a.MyMethod()
' Property usage.
a.MyProperty += 1
Console.WriteLine("a.MyProperty = " & a.MyProperty)
a(1) = 1
' Event usage.
aInstance = a
End Sub
Sub MyHandler() Handles aInstance.MyEvent
Console.WriteLine("Test.MyHandler")
End Sub
End Module
有两个特定于类的修饰符, MustInherit 以及 NotInheritable。 指定两者都无效。
类基规范
类声明可能包括定义该类的直接基类型的基本类型规范。
ClassBase
: 'Inherits' NonArrayTypeName StatementTerminator
;
如果类声明没有显式基类型,则直接基类型是 Object隐式的。 例如:
Class Base
End Class
Class Derived
Inherits Base
End Class
基类型本身不能是类型参数,尽管它可能涉及范围内的类型参数。
Class C1(Of V)
End Class
Class C2(Of V)
Inherits V ' Error, type parameter used as base class
End Class
Class C3(Of V)
Inherits C1(Of V) ' OK: not directly inheriting from V.
End Class
类只能派生自 Object 类和类。 类派生自System.ValueType、 System.MulticastDelegateSystem.EnumSystem.Array或System.Delegate派生类无效。 泛型类不能派生自 System.Attribute 或派生自它的类。
每个类只有一个直接基类,禁止派生中的循环性。 无法从 NotInheritable 类派生,基类的辅助功能域必须与类本身的辅助功能域或超集相同。
类成员
类的成员由其类成员声明引入的成员以及从其直接基类继承的成员组成。
ClassMemberDeclaration
: NonModuleDeclaration
| EventMemberDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
| OperatorDeclaration
;
类成员声明可以具有Public、Protected、Friend或Protected FriendPrivate访问。 当类成员声明不包含访问修饰符时,该声明默认访问 Public ,除非它是变量声明;在这种情况下,它默认访问 Private 。
类成员的范围是成员声明所在的类正文,以及该类的约束列表(如果它是泛型且具有约束)。 如果成员具有Friend访问权限,则其范围扩展到同一程序中的任何派生类的类主体或任何已授予Friend访问权限的程序集,如果成员具有Public或ProtectedProtected Friend访问权限,其范围将扩展到任何程序中任何派生类的类主体。
结构
结构 是继承自 System.ValueType.. 结构类似于类,它们表示可以包含数据成员和函数成员的数据结构。 但是,与类不同,结构不需要堆分配。
StructureDeclaration
: Attributes? StructureModifier* 'Structure' Identifier
TypeParameterList? StatementTerminator
TypeImplementsClause*
StructMemberDeclaration*
'End' 'Structure' StatementTerminator
;
StructureModifier
: TypeModifier
| 'Partial'
;
对于类,两个变量可以引用同一对象,因此,一个变量上的作可能会影响另一个变量引用的对象。 使用结构时,变量都有其自己的非Shared 数据副本,因此无法对一个变量执行作来影响另一个数据,如以下示例所示:
Structure Point
Public x, y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
End Structure
鉴于上述声明,以下代码输出值 10:
Module Test
Sub Main()
Dim a As New Point(10, 10)
Dim b As Point = a
a.x = 100
Console.WriteLine(b.x)
End Sub
End Module
将a赋值给b会创建一个值的副本,因此b不受将其赋值给a.x的影响。 相反,如果 Point 声明为类,输出将是 100 因为 a 并 b 引用相同的对象。
结构成员
结构的成员是由其结构成员声明和继承自 System.ValueType的成员引入的成员。
StructMemberDeclaration
: NonModuleDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| EventMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
| OperatorDeclaration
;
每个结构隐式都有一个 Public 无参数实例构造函数,该构造函数生成结构的默认值。 因此,结构类型声明不可能声明无参数实例构造函数。 但是,允许结构类型声明 参数化 实例构造函数,如以下示例所示:
Structure Point
Private x, y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
End Structure
给定上述声明,以下语句将创建 Point with x 和 y initialized 为零。
Dim p1 As Point = New Point()
Dim p2 As Point = New Point(0, 0)
由于结构直接包含其字段值(而不是对这些值的引用),因此结构不能包含直接或间接引用自己的字段。 例如,以下代码无效:
Structure S1
Dim f1 As S2
End Structure
Structure S2
' This would require S1 to contain itself.
Dim f1 As S1
End Structure
通常,结构成员声明只能具有Public、Friend或Private访问,但在重写继承自Object的成员时Protected Friend,Protected也可以使用访问。 当结构成员声明不包括访问修饰符时,声明默认为 Public 访问。 由结构声明的成员的范围是声明所在的结构主体,以及该结构的约束(如果它是泛型且具有约束)。
标准模块
标准模块是一种类型,其成员隐Shared式限定为标准模块包含命名空间的声明空间,而不仅仅是标准模块声明本身。 标准模块可能永远不会实例化。 声明标准模块类型的变量是错误的。
ModuleDeclaration
: Attributes? TypeModifier* 'Module' Identifier StatementTerminator
ModuleMemberDeclaration*
'End' 'Module' StatementTerminator
;
标准模块的成员有两个完全限定的名称,一个没有标准模块名称,一个具有标准模块名称。 命名空间中的多个标准模块可以定义具有特定名称的成员;对任一模块外部名称的非限定引用不明确。 例如:
Namespace N1
Module M1
Sub S1()
End Sub
Sub S2()
End Sub
End Module
Module M2
Sub S2()
End Sub
End Module
Module M3
Sub Main()
S1() ' Valid: Calls N1.M1.S1.
N1.S1() ' Valid: Calls N1.M1.S1.
S2() ' Not valid: ambiguous.
N1.S2() ' Not valid: ambiguous.
N1.M2.S2() ' Valid: Calls N1.M2.S2.
End Sub
End Module
End Namespace
模块只能在命名空间中声明,不能嵌套在另一种类型中。 标准模块可能无法实现接口,它们隐式派生自 Object,并且只有 Shared 构造函数。
标准模块成员
标准模块的成员是由其成员声明和继承自 Object的成员引入的成员。 标准模块可能具有除实例构造函数以外的任何类型的成员。 所有标准模块类型成员都是 Shared隐式的。
ModuleMemberDeclaration
: NonModuleDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| EventMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
;
通常,标准模块成员声明只能具有 Public、 Friend或 Private 访问,但当重写继承自 Object的成员时, Protected 可以指定访问 Protected Friend 修饰符。 如果标准模块成员声明不包含访问修饰符,则声明默认访问 Public ,除非它是一个默认 Private 访问变量。
如前所述,标准模块成员的范围是包含标准模块声明的声明。 继承自 Object 的成员不包括在此特殊范围中;这些成员没有作用域,并且必须始终使用模块的名称进行限定。 如果该成员具有 Friend 访问权限,则其范围仅扩展到在给定 Friend 访问权限的相同程序或程序集中声明的命名空间成员。
接口
接口 是其他类型的引用类型,可保证它们支持某些方法。 接口永远不会直接创建,并且没有实际表示形式 -- 必须将其他类型的转换为接口类型。 接口定义协定。 实现接口的类或结构必须遵循其协定。
InterfaceDeclaration
: Attributes? TypeModifier* 'Interface' Identifier
TypeParameterList? StatementTerminator
InterfaceBase*
InterfaceMemberDeclaration*
'End' 'Interface' StatementTerminator
;
以下示例显示了一个接口,该接口包含默认属性 Item、事件 E、方法和 F属性 P:
Interface IExample
Default Property Item(index As Integer) As String
Event E()
Sub F(value As Integer)
Property P() As String
End Interface
接口可能采用多个继承。 在以下示例中,接口IComboBox继承自这两个接口:ITextBoxIListBox:
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Interface IListBox
Inherits IControl
Sub SetItems(items() As String)
End Interface
Interface IComboBox
Inherits ITextBox, IListBox
End Interface
类和结构可以实现多个接口。 在以下示例中,该类EditBox派生自该类Control并实现这两个类IControl:IDataBound
Interface IDataBound
Sub Bind(b As Binder)
End Interface
Public Class EditBox
Inherits Control
Implements IControl, IDataBound
Public Sub Paint() Implements IControl.Paint
...
End Sub
Public Sub Bind(b As Binder) Implements IDataBound.Bind
...
End Sub
End Class
接口继承
接口的基本接口是显式基接口及其基接口。 换而言之,基接口集是显式基接口、它们的显式基接口等的完全传递闭包。 如果接口声明没有显式接口基,则类型没有基接口 -- 接口不继承( Object 尽管它们确实有扩大转换到 Object)。
InterfaceBase
: 'Inherits' InterfaceBases StatementTerminator
;
InterfaceBases
: NonArrayTypeName ( Comma NonArrayTypeName )*
;
在以下示例中,基接口 IComboBox 是 IControl, ITextBox以及 IListBox。
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Interface IListBox
Inherits IControl
Sub SetItems(items() As String)
End Interface
Interface IComboBox
Inherits ITextBox, IListBox
End Interface
接口继承其基接口的所有成员。 换句话说,上面的 IComboBox 接口继承成员 SetText 和 SetItems 以及 Paint。
实现接口的类或结构也隐式实现接口的所有基接口。
如果接口在基接口的可传递关闭中出现多次,则它只向派生接口贡献其成员一次。 实现派生接口的类型只需实现一次乘法基接口的方法。 在以下示例中, Paint 只需实现一次,即使类实现 IComboBox 并 IControl实现。
Class ComboBox
Implements IControl, IComboBox
Sub SetText(text As String) Implements IComboBox.SetText
End Sub
Sub SetItems(items() As String) Implements IComboBox.SetItems
End Sub
Sub Print() Implements IComboBox.Paint
End Sub
End Class
子 Inherits 句对其他 Inherits 子句没有影响。 在下面的示例中,IDerived必须限定其INested名称。IBase
Interface IBase
Interface INested
Sub Nested()
End Interface
Sub Base()
End Interface
Interface IDerived
Inherits IBase, INested ' Error: Must specify IBase.INested.
End Interface
基接口的辅助功能域必须与接口本身的辅助功能域或超集相同。
接口成员
接口的成员由其成员声明引入的成员和从其基接口继承的成员组成。
InterfaceMemberDeclaration
: NonModuleDeclaration
| InterfaceEventMemberDeclaration
| InterfaceMethodMemberDeclaration
| InterfacePropertyMemberDeclaration
;
虽然接口不继承成员Object,因为实现接口的每个类或结构都继承自Object接口,Object但包括扩展方法的成员被视为接口的成员,可以直接在接口上调用,而无需强制转换。Object 例如:
Interface I1
End Interface
Class C1
Implements I1
End Class
Module Test
Sub Main()
Dim i As I1 = New C1()
Dim h As Integer = i.GetHashCode()
End Sub
End Module
与隐式阴影Object成员成员同名的Object接口的成员。 只有嵌套类型、方法、属性和事件是接口的成员。 方法和属性可能没有正文。 接口成员是 Public 隐式的,不能指定访问修饰符。 接口中声明的成员的范围是声明所在的接口正文,以及该接口的约束列表(如果它是泛型且具有约束)。
数组
数组是一个引用类型,它包含通过一对一方式使用数组中变量顺序的索引访问的变量。 数组中包含的变量(也称为数组的 元素 )必须都属于同一类型,并且此类型称为数组的 元素类型 。
ArrayTypeName
: NonArrayTypeName ArrayTypeModifiers
;
ArrayTypeModifiers
: ArrayTypeModifier+
;
ArrayTypeModifier
: OpenParenthesis RankList? CloseParenthesis
;
RankList
: Comma*
;
ArrayNameModifier
: ArrayTypeModifiers
| ArraySizeInitializationModifier
;
创建数组实例时,数组的元素就存在,在销毁数组实例时不再存在。 数组的每个元素都初始化为其类型的默认值。 该类型 System.Array 是所有数组类型的基类型,不能实例化。 每个数组类型继承类型 System.Array 声明的成员,并且可转换为它(和 Object)。 具有元素 T 的一维数组类型也实现接口 System.Collections.Generic.IList(Of T) ; IReadOnlyList(Of T)如果 T 为引用类型,则数组类型也实现 IList(Of U) , IReadOnlyList(Of U) 并且对于 U 任何具有扩大引用转换的 T数组类型。
数组具有确定与每个数组元素关联的索引数的 排名 。 数组的排名决定了数组的 维度 数。 例如,排名为一的数组称为单维数组,并且排名大于一的数组称为多维数组。
以下示例创建一个整数值的一维数组,初始化数组元素,然后输出每个元素:
Module Test
Sub Main()
Dim arr(5) As Integer
Dim i As Integer
For i = 0 To arr.Length - 1
arr(i) = i * i
Next i
For i = 0 To arr.Length - 1
Console.WriteLine("arr(" & i & ") = " & arr(i))
Next i
End Sub
End Module
程序输出以下内容:
arr(0) = 0
arr(1) = 1
arr(2) = 4
arr(3) = 9
arr(4) = 16
arr(5) = 25
数组的每个维度都有关联的长度。 维度长度不是数组类型的一部分,而是在运行时创建数组类型的实例时建立的。 维度的长度确定该维度的有效索引范围:对于长度 N的维度,索引的范围可以是零到 N-1。 如果维度的长度为零,则该维度没有有效的索引。 数组中的元素总数是数组中每个维度长度的乘积。 如果数组的任何维度的长度为零,则表示该数组为空。 数组的元素类型可以是任何类型的。
通过向现有类型名称添加修饰符来指定数组类型。 修饰符由左括号、一组零个或多个逗号和右括号组成。 修改的类型是数组的元素类型,维度数是逗号加上一个。 如果指定了多个修饰符,则数组的元素类型为数组。 修饰符从左到右读取,最左侧的修饰符是最外部的数组。 在示例中
Module Test
Dim arr As Integer(,)(,,)()
End Module
元素类型 arr 是一维数组的三维数组的二维数组 Integer。
也可以通过将数组类型修饰符或数组大小的初始化修饰符放在变量名称上来声明为数组类型。 在这种情况下,数组元素类型是声明中给定的类型,数组维度由变量名称修饰符确定。 为清楚起见,对变量名称和同一声明中的类型名称具有数组类型修饰符无效。
以下示例演示了将数组类型用作元素类型的 Integer 各种局部变量声明:
Module Test
Sub Main()
Dim a1() As Integer ' Declares 1-dimensional array of integers.
Dim a2(,) As Integer ' Declares 2-dimensional array of integers.
Dim a3(,,) As Integer ' Declares 3-dimensional array of integers.
Dim a4 As Integer() ' Declares 1-dimensional array of integers.
Dim a5 As Integer(,) ' Declares 2-dimensional array of integers.
Dim a6 As Integer(,,) ' Declares 3-dimensional array of integers.
' Declare 1-dimensional array of 2-dimensional arrays of integers
Dim a7()(,) As Integer
' Declare 2-dimensional array of 1-dimensional arrays of integers.
Dim a8(,)() As Integer
Dim a9() As Integer() ' Not allowed.
End Sub
End Module
数组类型名称修饰符扩展到其后面的所有括号。 这意味着,在类型名称之后允许一组括在括号中的参数的情况下,无法指定数组类型名称的参数。 例如:
Module Test
Sub Main()
' This calls the Integer constructor.
Dim x As New Integer(3)
' This declares a variable of Integer().
Dim y As Integer()
' This gives an error.
' Array sizes can not be specified in a type name.
Dim z As Integer()(3)
End Sub
End Module
在最后一种情况下, (3) 解释为类型名称的一部分,而不是一组构造函数参数。
代表
委托是引用类型或对象的实例方法的引用类型Shared。
DelegateDeclaration
: Attributes? TypeModifier* 'Delegate' MethodSignature StatementTerminator
;
MethodSignature
: SubSignature
| FunctionSignature
;
其他语言中委托最接近的等效项是函数指针,但函数指针只能引用 Shared 函数,但委托可以同时引用 Shared 和实例方法。 在后一种情况下,委托不仅存储对方法入口点的引用,还存储对调用该方法的对象实例的引用。
委托声明可能没有 Handles 子句、子 Implements 句、方法正文或 End 构造。 委托声明的参数列表可能不包含 Optional 或 ParamArray 参数。 返回类型和参数类型的辅助功能域必须与委托本身的辅助功能域或超集相同。
委托的成员是从类 System.Delegate继承的成员。 委托还定义了以下方法:
一个构造函数,它采用两个参数,一个类型
Object,一个类型System.IntPtr。与
Invoke委托具有相同签名的方法。BeginInvoke签名为委托签名的方法,有三个差异。 首先,返回类型更改为System.IAsyncResult。 其次,将另外两个参数添加到参数列表的末尾:第一个类型System.AsyncCallback和第二个类型Object。 最后,所有ByRef参数都更改为ByVal。返回
EndInvoke类型与委托相同的方法。 该方法的参数只是完全是ByRef参数的委托参数,其顺序与在委托签名中发生的顺序相同。 除了这些参数,参数列表末尾还有一个类型System.IAsyncResult的其他参数。
定义和使用委托有三个步骤:声明、实例化和调用。
委托是使用委托声明语法声明的。 以下示例声明一个不带参数的委托 SimpleDelegate :
Delegate Sub SimpleDelegate()
下一个示例创建一个 SimpleDelegate 实例,然后立即调用它:
Module Test
Sub F()
System.Console.WriteLine("Test.F")
End Sub
Sub Main()
Dim d As SimpleDelegate = AddressOf F
d()
End Sub
End Module
实例化方法的委托并立即通过委托进行调用并不多,因为直接调用该方法会更简单。 当使用匿名时,代理人会显示其有用性。 下一个示例演示了一个 MultiCall 反复调用 SimpleDelegate 实例的方法:
Sub MultiCall(d As SimpleDelegate, count As Integer)
Dim i As Integer
For i = 0 To count - 1
d()
Next i
End Sub
该方法的目标方法SimpleDelegate、此方法的可访问性,或该方法是否具有Shared辅助功能,这并不重要MultiCall。 重要的是目标方法的签名与 SimpleDelegate.
分部类型
类和结构声明可以是 分部 声明。 分部声明可能或不能完全描述声明中的声明类型。 相反,该类型的声明可以分散在程序中的多个部分声明中;分部类型不能跨程序边界声明。 分部类型声明指定 Partial 声明的修饰符。 然后,对于具有相同完全限定名称的类型,程序中的任何其他声明都将与编译时的部分声明合并在一起,形成单个类型声明。 例如,以下代码声明一个 Test 包含成员 Test.C1 和 Test.C2.
a.vb:
Public Partial Class Test
Public Sub S1()
End Sub
End Class
b.vb:
Public Class Test
Public Sub S2()
End Sub
End Class
组合部分类型声明时,至少有一个声明必须具有 Partial 修饰符,否则会生成编译时错误结果。
注意。 尽管可以在许多部分声明中仅指定 Partial 一个声明,但最好在所有分部声明上指定声明。 如果一个部分声明可见,但隐藏了一个或多个部分声明(例如扩展工具生成的代码的情况),则可以接受将修饰符从可见声明中保留 Partial ,但在隐藏声明中指定它。
只能使用分部声明声明类和结构。 将分部声明匹配在一起时,将考虑类型的 arity:两个具有相同名称但类型参数数量不同的类不被视为同一时间的分部声明。 分部声明可以指定属性、类修饰符、 Inherits 语句或 Implements 语句。 在编译时,部分声明的所有部分组合在一起,并用作类型声明的一部分。 如果属性、修饰符、基数、接口或类型成员之间存在任何冲突,则编译时错误结果。 例如:
Public Partial Class Test1
Implements IDisposable
End Class
Class Test1
Inherits Object
Implements IComparable
End Class
Public Partial Class Test2
End Class
Private Partial Class Test2
End Class
上一个示例声明一个类型,该Public类型Test1继承自Object实现和System.IComparable实现System.IDisposable。 部分声明Test2将导致编译时错误,因为其中一个声明说,Test2也就是说Public,另一个声明就是Test2Private这样。
具有类型参数的分部类型可以声明类型参数的约束和方差,但每个分部声明的约束和方差必须匹配。 因此,约束和方差特别,因为它们与其他修饰符一样不会自动组合:
Partial Public Class List(Of T As IEnumerable)
End Class
' Error: Constraints on T don't match
Class List(Of T As IComparable)
End Class
使用多个分部声明声明类型的事实不会影响类型内的名称查找规则。 因此,分部类型声明可以使用在其他分部类型声明中声明的成员,也可以对在其他分部类型声明中声明的接口实现方法。 例如:
Public Partial Class Test1
Implements IDisposable
Private IsDisposed As Boolean = False
End Class
Class Test1
Private Sub Dispose() Implements IDisposable.Dispose
If Not IsDisposed Then
...
End If
End Sub
End Class
嵌套类型也可以具有部分声明。 例如:
Public Partial Class Test
Public Partial Class NestedTest
Public Sub S1()
End Sub
End Class
End Class
Public Partial Class Test
Public Partial Class NestedTest
Public Sub S2()
End Sub
End Class
End Class
分部声明中的初始值设定项仍按声明顺序执行;但是,对于在单独的分部声明中发生的初始值设定项,没有保证的执行顺序。
构造类型
泛型类型声明本身并不表示类型。 相反,泛型类型声明可以用作“蓝图”,通过应用类型参数来形成许多不同类型的类型。 应用了类型参数的泛型类型称为 构造类型。 构造类型中的类型参数必须始终满足它们匹配的类型参数上放置的约束。
即使类型名称未直接指定类型参数,类型名称也可能标识构造的类型。 如果类型嵌套在泛型类声明中,并且包含声明的实例类型隐式用于名称查找,则可能会出现这种情况:
Class Outer(Of T)
Public Class Inner
End Class
' Type of i is the constructed type Outer(Of T).Inner
Public i As Inner
End Class
当泛型类型和所有类型参数都可访问时,可访问构造的类型 C(Of T1,...,Tn) 。 例如,如果泛型类型 C 为 Public 和所有类型参数 T1,...,Tn , Public则构造的类型为 Public。 但是,如果类型名称或类型参数之一是 Private,则构造类型的可访问性为 Private。 如果构造类型的 Protected 一个类型参数是另一个类型参数, Friend则构造的类型只能在该类及其子类中访问此程序集或已授予 Friend 访问权限的任何程序集中。 换句话说,构造类型的辅助功能域是其构成部分的辅助功能域的交集。
注意。 构造类型的辅助功能域是其构成部分的交集,具有定义新的辅助功能级别的有趣副作用。 一个构造类型,其中包含一个元素,Protected并且只能在可以访问和FriendProtected成员的上下文中访问该元素Friend。 但是,无法以语言表达此辅助功能级别,因为辅助Protected Friend功能意味着可以在可以访问任Friend一或Protected成员的上下文中访问实体。
构造类型的基接口和成员是通过替换泛型类型中类型参数的每个匹配项提供的类型参数来确定的。
打开类型和关闭类型
一个或多个类型参数是包含类型或方法的类型参数的构造类型称为 开放类型。 这是因为该类型的某些类型参数仍然未知,因此该类型的实际形状尚不完全已知。 相比之下,其类型参数都是非类型参数的泛型类型称为 封闭类型。 封闭类型的形状始终是完全已知的。 例如:
Class Base(Of T, V)
End Class
Class Derived(Of V)
Inherits Base(Of Integer, V)
End Class
Class MoreDerived
Inherits Derived(Of Double)
End Class
构造类型是一种开放类型 Base(Of Integer, V) ,因为尽管类型参数 T 已提供,但类型参数已提供另一个类型参数 U 。 因此,该类型的完整形状尚不得而知。 但是,构造的类型是一种封闭类型 Derived(Of Double),因为继承层次结构中的所有类型参数都已提供。
开放类型的定义如下:
类型参数是打开的类型。
如果数组类型为开放类型,则数组类型为打开类型。
如果一个或多个构造类型参数是开放类型,则构造类型是一种打开类型。
封闭类型是不是打开类型的类型。
由于程序入口点不能位于泛型类型中,因此在运行时使用的所有类型都将是关闭类型。
特殊类型
.NET Framework 包含许多类,这些类由 .NET Framework 和 Visual Basic 语言专门处理:
表示 .NET Framework 中的 void 类型的类型 System.Void只能在表达式中 GetType 直接引用。
类型 System.RuntimeArgumentHandle以及 System.ArgIteratorSystem.TypedReference 所有类型都可以包含指向堆栈的指针,因此无法在 .NET Framework 堆上显示。 因此,它们不能用作数组元素类型、返回类型、字段类型、泛型类型自变量、可为 null 的类型、ByRef参数类型、要转换为ObjectSystem.ValueType的值的类型,或者调用实例成员ObjectSystem.ValueType的目标,或提升到关闭中。