表达式是一系列运算符和作数,用于指定值的计算,或指定变量或常量。 本章定义作数和运算符的计算顺序以及表达式的含义。
Expression
: SimpleExpression
| TypeExpression
| MemberAccessExpression
| DictionaryAccessExpression
| InvocationExpression
| IndexExpression
| NewExpression
| CastExpression
| OperatorExpression
| ConditionalExpression
| LambdaExpression
| QueryExpression
| XMLLiteralExpression
| XMLMemberAccessExpression
;
表达式分类
每个表达式都归类为下列表达式之一:
一个 值。 每个值都有一个关联的类型。
变量。 每个变量都有一个关联的类型,即变量的声明类型。
命名空间。 具有此分类的表达式只能显示为成员访问的左侧。 在任何其他上下文中,分类为命名空间的表达式会导致编译时错误。
类型。 具有此分类的表达式只能显示为成员访问的左侧。 在任何其他上下文中,分类为类型的表达式会导致编译时错误。
一个方法组, 这是在同一名称上重载的一组方法。 方法组可能具有关联的目标表达式和关联的类型参数列表。
表示方法位置的方法指针。 方法指针可能具有关联的目标表达式和关联的类型参数列表。
lambda 方法, 它是一种匿名方法。
一个属性组, 它是在同一名称上重载的一组属性。 属性组可能具有关联的目标表达式。
属性访问。 每个属性访问都有一个相关类型,即属性类型。 属性访问可能具有关联的目标表达式。
延迟访问, 表示在运行时延迟的方法或属性访问。 后期绑定访问可能具有关联的目标表达式和关联的类型参数列表。 后期绑定访问的类型始终
Object为 。事件访问。 每个事件访问都有一个关联类型,即事件的类型。 事件访问可能具有关联的目标表达式。 事件访问可能显示为和
AddHandlerRemoveHandler语句的第一个参数RaiseEvent。 在任何其他上下文中,分类为事件访问的表达式会导致编译时错误。数组文本, 表示尚未确定其类型的数组的初始值。
无效。 当表达式是子例程的调用或没有结果的 await 运算符表达式时,将发生这种情况。 分类为 void 的表达式仅在调用语句或 await 语句的上下文中有效。
默认值。 仅文本
Nothing生成此分类。
表达式的最终结果通常是值或变量,其他类别的表达式充当仅在某些上下文中允许的中间值。
请注意,如果对类型参数施加的约束满足这些特征,则其类型为类型参数的表达式可用于语句和表达式,这些表达式需要具有某些特征(如引用类型、值类型、派生自某些类型等)。
表达式重新分类
通常,在需要与表达式分类不同的上下文中使用表达式时,会发生编译时错误,例如,尝试向文本赋值。 但是,在许多情况下,可以通过 重新分类过程更改表达式的分类。
如果重新分类成功,则重新分类被判断为扩大或缩小。 除非另有说明,否则此列表中的所有重新分类都会扩大。
可以重新分类以下类型的表达式:
可以将变量重新分类为值。 提取存储在变量中的值。
方法组可以重新分类为值。 方法组表达式被解释为具有关联目标表达式和类型参数列表的调用表达式,以及空括号(即解释
f为f()并f(Of Integer)解释为f(Of Integer)())。 这种重新分类可能会导致表达式进一步重新分类为 void。方法指针可以重新分类为值。 这种重新分类只能在已知目标类型的转换上下文中发生。 方法指针表达式解释为具有关联类型参数列表的相应类型的委托实例化表达式的参数。 例如:
Delegate Sub D(i As Integer) Module Test Sub F(i As Integer) End Sub Sub Main() Dim del As D ' The next two lines are equivalent. del = AddressOf F del = New D(AddressOf F) End Sub End Modulelambda 方法可以重新分类为值。 如果重新分类发生在已知目标类型的转换上下文中,则可能会出现以下两个重新分类之一:
如果目标类型是委托类型,lambda 方法将解释为相应类型的委托构造表达式的参数。
如果目标类型为
System.Linq.Expressions.Expression(Of T)委托类型,并且T是委托类型,则 lambda 方法被解释为在委托构造表达式T中使用,然后转换为表达式树。
如果委托没有 ByRef 参数,则异步或迭代器 lambda 方法只能解释为委托构造表达式的参数。
如果从任何委托的参数类型转换为相应的 lambda 参数类型是收缩转换,则重新分类将判断为缩小;否则,它正在扩大。
注意。 lambda 方法和表达式树之间的确切转换在编译器版本之间可能不是固定的,并且超出了此规范的范围。 对于 Microsoft Visual Basic 11.0,所有 lambda 表达式都可以转换为受以下限制约束的表达式树:(1) 1。 只能将没有 ByRef 参数的单行 lambda 表达式转换为表达式树。 在单行
Sublambda 中,只能将调用语句转换为表达式树。 (2) 如果早期字段初始值设定项用于初始化后续字段初始值设定项,则匿名类型表达式不能转换为表达式树,例如New With {.a=1, .b=.a}。 (3) 如果在某个字段初始值设定项中使用当前对象的成员,则不能将对象初始值设定项表达式转换为表达式树。New C1 With {.a=1, .b=.Method1()}(4) 仅当多维数组创建表达式显式声明其元素类型时,才能转换为表达式树。 (5) 后期绑定表达式无法转换为表达式树。 (6) 将变量或字段传递给调用表达式但与 ByRef 参数的类型不完全相同,或者当属性通过 ByRef 传递时,正常的 VB 语义是参数的副本传递 ByRef,然后将其最终值复制回变量或字段或属性。 在表达式树中,复制回不会发生。 (7) 所有这些限制也适用于嵌套 lambda 表达式。如果目标类型未知,则 lambda 方法将解释为具有 lambda 方法同一签名的匿名委托类型的委托实例化表达式的参数。 如果使用严格的语义并省略任何参数的类型,则会发生编译时错误;否则,
Object将替换为任何缺失的参数类型。 例如:Module Test Sub Main() ' Type of x will be equivalent to Func(Of Object, Object, Object) Dim x = Function(a, b) a + b ' Type of y will be equivalent to Action(Of Object, Object) Dim y = Sub(a, b) Console.WriteLine(a + b) End Sub End Module可以将属性组重新分类为属性访问。 属性组表达式被解释为带空括号的索引表达式(即
f,解释为f())。可以将属性访问重新分类为值。 属性访问表达式被解释为属性访问器的调用表达式
Get。 如果该属性没有 getter,则会发生编译时错误。可将后期绑定访问重新分类为后期绑定方法或后期绑定属性访问。 在可将后期绑定访问重新分类为方法访问和属性访问的情况下,首选对属性访问的重新分类。
可将后期绑定访问重新分类为值。
数组文本可以重新分类为值。 值的类型按如下方式确定:
如果重新分类发生在目标类型已知且目标类型为数组类型的转换上下文中,则将数组文本重新分类为类型 T()。 如果目标类型为、、、或
IReadOnlyCollection(Of T)IEnumerable(Of T)数组文本具有一个嵌套级别,则将数组文本重新分类为类型的T()值。ICollection(Of T)IReadOnlyList(Of T)System.Collections.Generic.IList(Of T)否则,数组文本将重新分类为一个值,其类型为等于嵌套级别的数组,其元素类型由初始值设定项中的元素的主要类型确定:如果未确定主类型,
Object则使用。 例如:' x Is GetType(Double(,,)) Dim x = { { { 1, 2.0 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } }.GetType() ' y Is GetType(Integer()) Dim y = { 1, 2, 3 }.GetType() ' z Is GetType(Object()) Dim z = { 1, "2" }.GetType() ' Error: Inconsistent nesting Dim a = { { 10 }, { 20, 30 } }.GetType()
注意。 版本 9.0 和语言版本 10.0 之间的行为略有变化。 在 10.0 之前,数组元素初始值设定项不会影响局部变量类型推理,现在它们确实如此。 因此
Dim a() = { 1, 2, 3 },将推断Object()为语言版本 9.0 和Integer()版本 10.0 中的类型a。然后,重新分类会将数组文本重新解释为数组创建表达式。 因此,示例如下:
Dim x As Double = { 1, 2, 3, 4 } Dim y = { "a", "b" }等效于:
Dim x As Double = New Double() { 1, 2, 3, 4 } Dim y = New String() { "a", "b" }如果从元素表达式到数组元素类型的任何转换正在缩小,则重新分类被判断为缩小;否则,它被判断为扩大。
默认值
Nothing可以重新分类为值。 在已知目标类型的上下文中,结果是目标类型的默认值。 在目标类型未知的上下文中,结果是类型Object为 null 值。
命名空间表达式、类型表达式、事件访问表达式或 void 表达式无法重新分类。 可以同时执行多个重新分类。 例如:
Module Test
Sub F(i As Integer)
End Sub
ReadOnly Property P() As Integer
Get
End Get
End Sub
Sub Main()
F(P)
End Property
End Module
在这种情况下,属性组表达式 P 首先从属性组重新分类到属性访问,然后从属性访问值重新分类。 执行最少的重新分类以在上下文中达到有效分类。
常量表达式
常量表达式是一个表达式,其值可以在编译时完全计算。
ConstantExpression
: Expression
;
常量表达式的类型可以是Byte、、、ShortUShort、DateUIntegerIntegerLongDecimalDoubleSingleCharBooleanULong、String或Object任意枚举类型。 SByte 常量表达式中允许以下构造:
文本(包括
Nothing)。对常量类型成员或常量局部变量的引用。
对枚举类型成员的引用。
带括号的子表达式。
强制表达式,前提是目标类型是上面列出的类型之一。 强制转换
String是此规则的例外,仅允许对 null 值执行,因为String转换始终在运行时执行环境的当前区域性中完成。 请注意,常量强制表达式只能使用内部转换。+-如果作数和结果为上面列出的类型,则为Not一元运算符和一元运算符。如果每个作数和结果都由上面列出的类型,则
+、-、OrElse<*^&>>And<<Or\XorAndAlso/Mod><>=<=二=>元运算符以及二元运算符。条件运算符 If,前提是每个作数和结果都是上面列出的类型。
以下运行时函数:
Microsoft.VisualBasic.Strings.ChrW;Microsoft.VisualBasic.Strings.Chr如果常量值介于 0 和 128 之间,Microsoft.VisualBasic.Strings.AscW则为常量字符串;如果常量字符串不为空,Microsoft.VisualBasic.Strings.Asc则为空。
常量表达式 中不允许 使用以下构造:
- 通过上下文进行
With隐式绑定。
整型类型的常量表达式(ULong、LongIntegerUInteger、、ShortUShort或ByteSByte)可以隐式转换为较窄的整型,并且类型的常量表达式可以隐式转换为Single,前提是常量表达式Double的值在目标类型范围内。 无论使用宽松语义还是严格语义,都允许这些缩小转换。
Late-Bound 表达式
当成员访问表达式或索引表达式的目标的类型为类型 Object时,表达式的处理可能会延迟到运行时。 以这种方式延迟处理称为 后期绑定。 后期绑定允许 Object 以 无类型 方式使用变量,其中所有成员解析都基于变量中值的实际运行时类型。 如果编译环境或后期 Option Strict绑定指定了严格的语义,会导致编译时错误。 执行后期绑定时,将忽略非公共成员,包括出于重载解析的目的。 请注意,与早期绑定情况不同,调用或访问 Shared 成员后期绑定将导致在运行时评估调用目标。 如果表达式是定义 System.Object的成员的调用表达式,则不会进行后期绑定。
通常,后期绑定访问通过在表达式的实际运行时类型上查找标识符,在运行时解析。 如果在运行时延迟绑定成员查找失败, System.MissingMemberException 则会引发异常。 由于后期绑定成员查找仅关闭关联的目标表达式的运行时类型,因此对象的运行时类型从来不是接口。 因此,无法在后期绑定成员访问表达式中访问接口成员。
后期绑定成员访问的参数按成员访问表达式中显示的顺序计算:而不是在后期绑定成员中声明参数的顺序。 以下示例演示了此差异:
Class C
Public Sub f(ByVal x As Integer, ByVal y As Integer)
End Sub
End Class
Module Module1
Sub Main()
Console.Write("Early-bound: ")
Dim c As C = New C
c.f(y:=t("y"), x:=t("x"))
Console.Write(vbCrLf & "Late-bound: ")
Dim o As Object = New C
o.f(y:=t("y"), x:=t("x"))
End Sub
Function t(ByVal s As String) As Integer
Console.Write(s)
Return 0
End Function
End Module
此代码显示:
Early-bound: xy
Late-bound: yx
由于后期绑定重载解析是在参数的运行时类型上完成的,因此表达式可能会根据是在编译时还是运行时计算结果而生成不同的结果。 以下示例演示了此差异:
Class Base
End Class
Class Derived
Inherits Base
End Class
Module Test
Sub F(b As Base)
Console.WriteLine("F(Base)")
End Sub
Sub F(d As Derived)
Console.WriteLine("F(Derived)")
End Sub
Sub Main()
Dim b As Base = New Derived()
Dim o As Object = b
F(b)
F(o)
End Sub
End Module
此代码显示:
F(Base)
F(Derived)
简单表达式
简单表达式是文本、括号表达式、实例表达式或简单名称表达式。
SimpleExpression
: LiteralExpression
| ParenthesizedExpression
| InstanceExpression
| SimpleNameExpression
| AddressOfExpression
;
文本表达式
文本表达式的计算结果为文本表示的值。 文本表达式被分类为值,但文本除外,该文本 Nothing被分类为默认值。
LiteralExpression
: Literal
;
带圆括号表达式
括号表达式由括在括号中的表达式组成。 括号表达式被分类为值,封闭表达式必须分类为值。 括号表达式的计算结果为括号内的表达式的值。
ParenthesizedExpression
: OpenParenthesis Expression CloseParenthesis
;
实例表达式
实例表达式是关键字Me。 它只能在非共享方法、构造函数或属性访问器的正文中使用。 它被归类为值。 关键字 Me 表示包含要执行的方法或属性访问器的类型的实例。 如果构造函数显式调用另一个构造函数(Section 构造函数), Me 则在该构造函数调用之后才能使用,因为该实例尚未构造。
InstanceExpression
: 'Me'
;
简单名称表达式
简单名称表达式由一个标识符组成,后跟可选类型参数列表。
SimpleNameExpression
: Identifier ( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
该名称由以下“简单名称解析规则”解析和分类:
从立即封闭块开始,并继续每个封闭的外部块(如果有),如果标识符与局部变量、静态变量、常量局部变量、方法类型参数或参数的名称匹配,则标识符将引用匹配实体。
如果标识符与局部变量、静态变量或常量局部变量匹配,并且提供了类型参数列表,则会发生编译时错误。 如果标识符与方法类型参数匹配,并且提供了类型参数列表,则不会发生匹配并继续解析。 如果标识符与局部变量匹配,则匹配的局部变量是隐式函数或
Get访问器返回局部变量,并且表达式是调用表达式、调用语句或表达式的一AddressOf部分,则不会发生匹配,解析继续。如果表达式是局部变量、静态变量或参数,则将其分类为变量。 如果表达式是方法类型参数,则将其分类为类型。 如果表达式是常量局部值,则表达式被归类为值。
对于包含表达式的每个嵌套类型,从最内层开始并转到最外层,如果类型中的标识符查找生成与可访问成员的匹配项:
- 如果匹配类型成员是类型参数,则结果被分类为类型,并且是匹配的类型参数。 如果提供了类型参数列表,则不会发生匹配,并且解析继续。
- 否则,如果类型是立即封闭的类型,并且查找标识非共享类型成员,则结果与表单
Me.E(Of A)的成员访问相同,其中E标识符是标识符,并且A是类型参数列表(如果有)。 - 否则,结果与表单
T.E(Of A)的成员访问完全相同,其中T包含匹配成员E的类型是标识符,并且A是类型参数列表(如果有)。 在这种情况下,标识符引用非共享成员是错误的。
对于每个嵌套命名空间,从最内部开始并转到最外部的命名空间,请执行以下作:
- 如果命名空间包含具有给定名称的可访问类型,并且类型参数数量与类型参数列表中提供的类型参数数量相同(如果有),则标识符引用该类型,并归类为类型。
- 否则,如果未提供类型参数列表,并且命名空间包含具有给定名称的命名空间成员,则标识符将引用该命名空间,并将其分类为命名空间。
- 否则,如果命名空间包含一个或多个可访问的标准模块,并且标识符的成员名称查找在完全相同的一个标准模块中生成可访问匹配项,则结果与表单
M.E(Of A)的成员访问完全相同,其中M包含匹配成员的标准模块是标识符,E并且A是类型参数列表, 如果有。 如果标识符与多个标准模块中的可访问类型成员匹配,则会发生编译时错误。
如果源文件具有一个或多个导入别名,并且标识符与其中一个别名的名称匹配,则标识符将引用该命名空间或类型。 如果提供了类型参数列表,则会发生编译时错误。
如果包含名称引用的源文件具有一个或多个导入:
- 如果标识符恰好与类型参数列表中提供的类型参数数量相同,则标识符引用该类型或类型成员,则标识符将引用该类型或类型成员。 如果标识符在多个导入中匹配的可访问类型的名称,其类型参数数量与类型参数列表中提供的名称相同(如果有)或可访问的类型成员,则会发生编译时错误。
- 否则,如果未提供类型参数列表,并且标识符恰好在一个导入具有可访问类型的命名空间的名称中匹配,则标识符将引用该命名空间。 如果未提供类型参数列表,并且标识符与多个导入命名空间的名称匹配且具有可访问类型的命名空间的名称,则会发生编译时错误。
- 否则,如果导入包含一个或多个可访问的标准模块,并且标识符的成员名称查找在完全相同的一个标准模块中生成可访问匹配项,则结果与表单
M.E(Of A)的成员访问完全相同,其中M包含匹配成员的标准模块是标识符,E并且A是类型参数列表, 如果有。 如果标识符与多个标准模块中的可访问类型成员匹配,则会发生编译时错误。
如果编译环境定义了一个或多个导入别名,并且标识符与其中一个别名的名称匹配,则标识符将引用该命名空间或类型。 如果提供了类型参数列表,则会发生编译时错误。
如果编译环境定义了一个或多个导入:
- 如果标识符恰好与类型参数列表中提供的类型参数数量相同,则标识符引用该类型或类型成员,则标识符将引用该类型或类型成员。 如果标识符在多个导入中匹配的可访问类型的名称,其类型参数数与类型参数列表中提供的名称相同(如果有)或类型成员,则会发生编译时错误。
- 否则,如果未提供类型参数列表,并且标识符恰好在一个导入具有可访问类型的命名空间的名称中匹配,则标识符将引用该命名空间。 如果未提供类型参数列表,并且标识符与多个导入命名空间的名称匹配且具有可访问类型的命名空间的名称,则会发生编译时错误。
- 否则,如果导入包含一个或多个可访问的标准模块,并且标识符的成员名称查找在完全相同的一个标准模块中生成可访问匹配项,则结果与表单
M.E(Of A)的成员访问完全相同,其中M包含匹配成员的标准模块是标识符,E并且A是类型参数列表, 如果有。 如果标识符与多个标准模块中的可访问类型成员匹配,则会发生编译时错误。
否则,标识符给出的名称未定义。
未定义的简单名称表达式是编译时错误。
通常,名称只能在特定命名空间中发生一次。 但是,由于命名空间可以跨多个 .NET 程序集声明,因此,如果两个程序集定义了具有相同完全限定名称的类型,则有可能这样做。 在这种情况下,在当前源文件集中声明的类型优先于在外部 .NET 程序集中声明的类型。 否则,名称不明确,无法消除名称的歧义。
AddressOf 表达式
AddressOf表达式用于生成方法指针。 该表达式由 AddressOf 关键字和必须分类为方法组或后期绑定访问的表达式组成。 方法组不能引用构造函数。
结果被归类为方法指针,其关联的目标表达式和类型参数列表(如果有)与方法组相同。
AddressOfExpression
: 'AddressOf' Expression
;
类型表达式
类型表达式是GetType表达式、TypeOf...Is表达式、Is表达式或GetXmlNamespace表达式。
TypeExpression
: GetTypeExpression
| TypeOfIsExpression
| IsExpression
| GetXmlNamespaceExpression
;
GetType 表达式
表达式 GetType 由关键字 GetType 和类型的名称组成。
GetTypeExpression
: 'GetType' OpenParenthesis GetTypeTypeName CloseParenthesis
;
GetTypeTypeName
: TypeName
| QualifiedOpenTypeName
;
QualifiedOpenTypeName
: Identifier TypeArityList? (Period IdentifierOrKeyword TypeArityList?)*
| 'Global' Period IdentifierOrKeyword TypeArityList?
(Period IdentifierOrKeyword TypeArityList?)*
;
TypeArityList
: OpenParenthesis 'Of' CommaList? CloseParenthesis
;
CommaList
: Comma Comma*
;
GetType表达式被分类为值,其值为表示其 GetTypeTypeName 的反射类(System.Type)。 如果 GetTypeTypeName 是类型参数,则表达式将返回 System.Type 与运行时为类型参数提供的类型参数对应的对象。
GetTypeTypeName 以两种方式特别:
它允许
System.Void是唯一可以引用此类型名称的语言位置。它可能是一个构造的泛型类型,其中省略了类型参数。 这样,
GetType表达式就可以返回System.Type与泛型类型本身对应的对象。
以下示例演示表达式 GetType :
Module Test
Sub Main()
Dim t As Type() = { GetType(Integer), GetType(System.Int32), _
GetType(String), GetType(Double()) }
Dim i As Integer
For i = 0 To t.Length - 1
Console.WriteLine(t(i).Name)
Next i
End Sub
End Module
生成的输出为:
Int32
Int32
String
Double[]
TypeOf...是表达式
TypeOf...Is表达式用于检查值的运行时类型是否与给定类型兼容。 第一个作数必须分类为值,不能是重新分类的 lambda 方法,并且必须是引用类型或不受约束的类型参数类型。 第二个作数必须是类型名称。 表达式的结果被分类为一个值,并且是一个 Boolean 值。 如果作数的运行时类型具有标识、默认、引用、数组、值类型或类型参数转换为类型,
TypeOfIsExpression
: 'TypeOf' Expression 'Is' LineTerminator? TypeName
;
是表达式
或IsIsNot表达式用于执行引用相等比较。
IsExpression
: Expression 'Is' LineTerminator? Expression
| Expression 'IsNot' LineTerminator? Expression
;
每个表达式必须分类为一个值,并且每个表达式的类型必须是引用类型、不受约束的类型参数类型或可为 null 的值类型。 但是,如果一个表达式的类型是不受约束的类型参数类型或可以为 null 的值类型,则另一个表达式必须是文本 Nothing。
结果被分类为值,类型化为 Boolean。 如果两个Is值都引用同一实例或两个值,或者False否则,作的计算True结果为Nothing。 如果两个IsNot值都引用同一实例或两个值,或者True否则,作的计算False结果为Nothing。
GetXmlNamespace 表达式
表达式 GetXmlNamespace 由关键字 GetXmlNamespace 和源文件或编译环境声明的 XML 命名空间的名称组成。
GetXmlNamespaceExpression
: 'GetXmlNamespace' OpenParenthesis XMLNamespaceName? CloseParenthesis
;
GetXmlNamespace表达式被分类为值,其值是表示 XMLNamespaceName 的System.Xml.Linq.XNamespace实例。 如果该类型不可用,则会发生编译时错误。
例如:
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
Dim db = GetXmlNamespace(db)
' The following are equivalent
Dim customer1 = _
New System.Xml.Linq.XElement(db + "customer", "Bob")
Dim customer2 = <db:customer>Bob</>
End Sub
End Module
括号之间的所有内容都被视为命名空间名称的一部分,因此有关空格等内容的 XML 规则适用。 例如:
Imports <xmlns:db-ns="http://example.org/database">
Module Test
Sub Main()
' Error, XML name expected
Dim db1 = GetXmlNamespace( db-ns )
' Error, ')' expected
Dim db2 = GetXmlNamespace(db _
)
' OK.
Dim db3 = GetXmlNamespace(db-ns)
End Sub
End Module
也可以省略 XML 命名空间表达式,在这种情况下,表达式返回表示默认 XML 命名空间的对象。
成员访问表达式
成员访问表达式用于访问实体的成员。
MemberAccessExpression
: MemberAccessBase? Period IdentifierOrKeyword
( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
MemberAccessBase
: Expression
| NonArrayTypeName
| 'Global'
| 'MyClass'
| 'MyBase'
;
表单 E.I(Of A)的成员访问权限,其中 E 是表达式、非数组类型名称、关键字 Global或省略,并且 I 是具有可选类型参数列表 A的标识符,计算并分类如下:
如果
E省略,则立即包含With语句中的表达式将被替换E并执行成员访问。 如果没有包含With语句,则会发生编译时错误。如果
E分类为命名空间或E关键字Global,则成员查找是在指定命名空间的上下文中完成的。 如果I命名空间的可访问成员的名称与类型参数列表中提供的类型参数数量相同(如果有),则结果是该成员。 结果根据成员分类为命名空间或类型。 否则,将发生编译时错误。如果
E类型或表达式分类为类型,则成员查找是在指定类型的上下文中完成的。 如果I为可访问成员E的名称,则E.I计算并分类如下:- 如果
I关键字NewE不是枚举,则会发生编译时错误。 - 如果
I标识的类型参数数量与类型参数列表中提供的类型相同,则结果为该类型。 - 如果
I标识一个或多个方法,则结果是一个方法组,其中包含关联的类型参数列表,并且没有关联的目标表达式。 - 如果
I标识一个或多个属性且未提供类型参数列表,则结果为没有关联的目标表达式的属性组。 - 如果
I标识共享变量且未提供类型参数列表,则结果为变量或值。 如果变量是只读的,并且引用发生在声明变量的类型共享构造函数之外,则结果为共享变量I的值E。 否则,结果是共享变量I。E - 如果
I标识共享事件且未提供类型参数列表,则结果是没有关联的目标表达式的事件访问。 - 如果
I标识常量且未提供类型参数列表,则结果为该常量的值。 - 如果
I标识枚举成员且未提供类型参数列表,则结果为该枚举成员的值。 - 否则,
E.I是无效的成员引用,并且会发生编译时错误。
- 如果
如果
E分类为变量或值,则其T类型为,则成员查找在上下文T中完成。 如果I为可访问成员T的名称,则E.I计算并分类如下:- 如果
I关键字New、E是MeMyBase、或MyClass没有提供类型参数,则结果是一个方法组,表示具有关联目标表达式E且E没有类型参数列表的类型的实例构造函数。 否则,将发生编译时错误。 - 如果
I标识一个或多个方法(包括扩展方法(如果T不是Object),则结果是一个方法组,其中包含关联的类型参数列表和关联的目标表达式E。 - 如果
I标识一个或多个属性且未提供类型参数,则结果为具有关联的目标表达式E的属性组。 - 如果
I标识共享变量或实例变量,并且未提供类型参数,则结果为变量或值。 如果变量是只读的,并且引用发生在类的构造函数之外,在该构造函数中声明该变量适合变量类型(共享或实例),则结果为对象E中引用的变量I的值。 如果T为引用类型,则结果为对象中引用的E变量I。 否则,如果T为值类型且表达式E被分类为变量,则结果为变量;否则结果为值。 - 如果
I标识事件且未提供类型参数,则结果是具有关联的目标表达式E的事件访问。 - 如果
I标识常量且未提供类型参数,则结果为该常量的值。 - 如果
I标识枚举成员且未提供类型参数,则结果为该枚举成员的值。 -
Object如果是T,则结果是后期绑定成员查找,其分类为具有关联类型自变量列表和关联的目标表达式的E后期绑定访问。
- 如果
否则,
E.I是无效的成员引用,并且会发生编译时错误。
表单的成员访问权限等效Me.I(Of A)于该窗体,但访问该表单MyClass.I(Of A)上的所有成员都被视为不可重写的成员。 因此,访问的成员不受要访问成员的值的运行时类型的影响。
表单MyBase.I(Of A)的成员访问等效于CType(Me, T).I(Of A)T包含成员访问表达式的类型的直接基类型。 其上的所有方法调用都被视为不可重写的方法。 此形式的成员访问也称为 基本访问。
以下示例演示了如何MeMyBase和MyClass关联:
Class Base
Public Overridable Sub F()
Console.WriteLine("Base.F")
End Sub
End Class
Class Derived
Inherits Base
Public Overrides Sub F()
Console.WriteLine("Derived.F")
End Sub
Public Sub G()
MyClass.F()
End Sub
End Class
Class MoreDerived
Inherits Derived
Public Overrides Sub F()
Console.WriteLine("MoreDerived.F")
End Sub
Public Sub H()
MyBase.F()
End Sub
End Class
Module Test
Sub Main()
Dim x As MoreDerived = new MoreDerived()
x.F()
x.G()
x.H()
End Sub
End Module
此代码输出:
MoreDerived.F
Derived.F
Derived.F
当成员访问表达式以关键字 Global开头时,关键字表示最外部的未命名命名空间,在声明隐藏封闭命名空间的情况下非常有用。 该 Global 关键字允许在这种情况下“转义”到最外部的命名空间。 例如:
Class System
End Class
Module Test
Sub Main()
' Error: Class System does not contain Console
System.Console.WriteLine("Hello, world!")
' Legal, binds to System in outermost namespace
Global.System.Console.WriteLine("Hello, world!")
End Sub
End Module
在上面的示例中,第一个方法调用无效,因为标识符 System 绑定到类 System,而不是命名空间 System。 访问 System 命名空间的唯一方法是用于 Global 转义到最外部的命名空间。
如果正在访问的成员是共享的,则句点左侧的任何表达式都是多余的,除非成员访问已晚绑定,否则不会计算该表达式。 例如,考虑以下代码:
Class C
Public Shared F As Integer = 10
End Class
Module Test
Public Function ReturnC() As C
Console.WriteLine("Returning a new instance of C.")
Return New C()
End Function
Public Sub Main()
Console.WriteLine("The value of F is: " & ReturnC().F)
End Sub
End Module
它打印The value of F is: 10是因为不需要调用该函数ReturnC来提供访问共享成员F的C实例。
相同的类型和成员名称
使用与其类型相同的名称命名成员并不少见。 但是,在这种情况下,可能会出现不方便的名称隐藏:
Enum Color
Red
Green
Yellow
End Enum
Class Test
ReadOnly Property Color() As Color
Get
Return Color.Red
End Get
End Property
Shared Function DefaultColor() As Color
Return Color.Green ' Binds to the instance property!
End Function
End Class
在前面的示例中,绑定到实例属性而不是DefaultColor类型的简单名称Color。 由于不能在共享成员中引用实例成员,因此这通常是一个错误。
但是,特殊规则允许在这种情况下访问该类型。 如果成员访问表达式的基表达式是一个简单的名称,并绑定到类型具有相同名称的常量、字段、属性、局部变量或参数,则基表达式可以引用该成员或类型。 这绝不会导致歧义,因为可以从任一成员中访问的成员是相同的。
默认实例
在某些情况下,派生自公共基类的类通常或始终只有一个实例。 例如,用户界面中显示的大多数窗口在任何时候都只在屏幕上显示一个实例。 为了简化使用这些类型的类,Visual Basic 可以自动生成为每个类提供单个且易于引用的实例的类 的默认实例 。
默认实例始终为一 系列 类型创建,而不是为一种特定类型创建。 因此,对于派生自 Form 的类 Form1,默认实例不是为派生自 Form 的所有类创建默认实例。 这意味着,派生自基类的每个类不必专门标记为具有默认实例。
类的默认实例由编译器生成的属性表示,该属性返回该类的默认实例。 作为名为 组类 的类的成员生成的属性,该类管理从特定基类派生的所有类的分配和销毁默认实例。 例如,可以在类中MyForms收集派生自Form的类的所有默认实例属性。 如果表达式 My.Forms返回组类的实例,则以下代码将访问派生类 Form1 的默认实例,并且 Form2:
Class Form1
Inherits Form
Public x As Integer
End Class
Class Form2
Inherits Form
Public y As Integer
End Class
Module Main
Sub Main()
My.Forms.Form1.x = 10
Console.WriteLine(My.Forms.Form2.y)
End Sub
End Module
在对默认实例的第一个引用之前,不会创建默认实例;提取表示默认实例的属性会导致创建默认实例(如果尚未创建或已设置为 Nothing该实例)。 若要允许测试默认实例是否存在,当默认实例是某个或IsNot运算符的目标Is时,将不会创建默认实例。 因此,可以测试默认实例 Nothing 是还是某些其他引用,而不会导致创建默认实例。
默认实例旨在使从具有默认实例的类外部轻松引用默认实例。 在定义默认实例的类中使用默认实例可能会导致混淆,例如,要引用哪个实例,即默认实例或当前实例。 例如,以下代码仅修改默认实例中的值 x ,即使从另一个实例调用该值也是如此。 因此,代码将输出值 5 ,而不是 10:
Class Form1
Inherits Form
Public x As Integer = 5
Public Sub ChangeX()
Form1.x = 10
End Sub
End Class
Module Main
Sub Main()
Dim f As Form1 = New Form1()
f.ChangeX()
Console.WriteLine(f.x)
End Sub
End Module
为防止这种混淆,从默认实例类型的实例方法中引用默认实例无效。
默认实例和类型名称
也可以直接通过其类型的名称访问默认实例。 在这种情况下,在任何不允许类型名称的表达式 E上下文中,如果 E 表达式表示具有默认实例的类的完全限定名称,则更改为 E',其中 E' 表示提取默认实例属性的表达式。 例如,如果派生自 Form 允许通过类型名称访问默认实例的类的默认实例,则以下代码等效于上一示例中的代码:
Module Main
Sub Main()
Form1.x = 10
Console.WriteLine(Form2.y)
End Sub
End Module
这也意味着可通过其类型名称访问的默认实例也可以通过类型名称进行分配。 例如,以下代码将默认实例 Form1 设置为 Nothing:
Module Main
Sub Main()
Form1 = Nothing
End Sub
End Module
请注意,其E含义E.I表示类,表示I共享成员不会更改。 此类表达式仍直接从类实例访问共享成员,并且不引用默认实例。
组类
该 Microsoft.VisualBasic.MyGroupCollectionAttribute 属性指示一系列默认实例的组类。 该属性有四个参数:
该参数
TypeToCollect指定组的基类。 不带开放类型参数的所有实例化类(无论类型参数如何)都将自动具有默认实例。该参数
CreateInstanceMethodName指定要在组类中调用的方法,以在默认实例属性中创建新实例。如果为默认实例属性赋
Nothing值,则参数DisposeInstanceMethodName指定在组类中调用的方法以释放默认实例属性。如果默认实例可通过其类型名称直接访问,则参数
DefaultInstanceAlias将指定用于替换类名的表达式E'。 如果此参数为Nothing空字符串,则无法直接通过其类型的名称访问此组类型的默认实例。 (注意。 在 Visual Basic 语言的所有当前实现中,DefaultInstanceAlias参数将被忽略,编译器提供的代码除外。
可以通过使用逗号分隔前三个参数中的类型和方法的名称,将多个类型收集到同一组中。 每个参数中必须具有相同数量的项,并且列表元素按顺序匹配。 例如,以下属性声明收集派生自C1C2或C3进入单个组的类型:
<Microsoft.VisualBasic.MyGroupCollection("C1, C2, C3", _
"CreateC1, CreateC2, CreateC3", _
"DisposeC1, DisposeC2, DisposeC3", "My.Cs")>
Public NotInheritable Class MyCs
...
End Class
create 方法的签名必须采用表单 Shared Function <Name>(Of T As {New, <Type>})(Instance Of T) As T。 dispose 方法必须采用窗体 Shared Sub <Name>(Of T As <Type>)(ByRef Instance Of T)。 因此,上一部分中示例的组类可以按如下方式声明:
<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
"Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
Private Shared Function Create(Of T As {New, Form}) _
(Instance As T) As T
If Instance Is Nothing Then
Return New T()
Else
Return Instance
End If
End Function
Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
Instance.Close()
Instance = Nothing
End Sub
End Class
如果源文件声明了派生类 Form1,生成的组类将等效于:
<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
"Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
Private Shared Function Create(Of T As {New, Form}) _
(Instance As T) As T
If Instance Is Nothing Then
Return New T()
Else
Return Instance
End If
End Function
Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
Instance.Close()
Instance = Nothing
End Sub
Private m_Form1 As Form1
Public Property Form1() As Form1
Get
Return Create(m_Form1)
End Get
Set (Value As Form1)
If Value IsNot Nothing AndAlso Value IsNot m_Form1 Then
Throw New ArgumentException( _
"Property can only be set to Nothing.")
End If
Dispose(m_Form1)
End Set
End Property
End Class
扩展方法集合
通过使用当前上下文中提供的名称I收集所有扩展方法来收集成员访问表达式E.I的扩展方法:
- 首先,检查包含表达式的每个嵌套类型,从最内层开始并转到最外层。
- 然后,检查每个嵌套命名空间,从最内部开始并转到最外部的命名空间。
- 然后,将检查源文件中的导入。
- 然后,检查编译环境定义的导入。
仅当有从目标表达式类型到扩展方法的第一个参数类型的扩展本机转换时,才会收集扩展方法。 与常规简单名称表达式绑定不同,搜索将收集 所有 扩展方法;找到扩展方法时,集合不会停止。 例如:
Imports System.Runtime.CompilerServices
Class C1
End Class
Namespace N1
Module N1C1Extensions
<Extension> _
Sub M1(c As C1, x As Integer)
End Sub
End Module
End Namespace
Namespace N1.N2
Module N2C1Extensions
<Extension> _
Sub M1(c As C1, y As Double)
End Sub
End Module
End Namespace
Namespace N1.N2.N3
Module Test
Sub Main()
Dim x As New C1()
' Calls N1C1Extensions.M1
x.M1(10)
End Sub
End Module
End Namespace
在此示例中,即使 N2C1Extensions.M1 之前 N1C1Extensions.M1找到它们,它们都被视为扩展方法。 收集所有扩展方法后,它们就会 得到治愈。 Currying 采用扩展方法调用的目标,并将其应用于扩展方法调用,导致删除了第一个参数的新方法签名(因为它已被指定)。 例如:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M(x As Integer, y As Integer)
End Sub
End Module
Module Ext2
<Extension> _
Sub M(x As Integer, y As Double)
End Sub
End Module
Module Main
Sub Test()
Dim v As Integer = 10
' The curried method signatures considered are:
' Ext1.M(y As Integer)
' Ext2.M(y As Double)
v.M(10)
End Sub
End Module
在上面的示例中,应用的 vExt1.M curried 结果是方法签名 Sub M(y As Integer)。
除了删除扩展方法的第一个参数外,currying 还会删除属于第一个参数类型的任何方法类型参数。 使用方法类型参数对扩展方法进行 curry 时,类型推理将应用于第一个参数,并且对于推断的任何类型参数,结果都是固定的。 如果类型推理失败,则忽略该方法。 例如:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M(Of T, U)(x As T, y As U)
End Sub
End Module
Module Ext2
<Extension> _
Sub M(Of T)(x As T, y As T)
End Sub
End Module
Module Main
Sub Test()
Dim v As Integer = 10
' The curried method signatures considered are:
' Ext1.M(Of U)(y As U)
' Ext2.M(y As Integer)
v.M(10)
End Sub
End Module
在上面的示例中,应用于vExt1.M的 curried 结果是方法签名Sub M(Of U)(y As U),因为类型参数T被推断为卷曲的结果,现在已修复。 由于类型参数 U 不是作为咖喱的一部分推断的,因此它仍然是一个打开的参数。 同样,由于类型参数T被推断为应用vExt2.M的结果,因此参数的类型y将变为固定类型Integer。 不会推断为任何其他类型。 对签名进行咖喱时,除了约束之外的所有约束 New 也会应用。 如果未满足约束,或依赖于未推断为卷曲的一部分的类型,则忽略扩展方法。 例如:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M1(Of T As Structure)(x As T, y As Integer)
End Sub
<Extension> _
Sub M2(Of T As U, U)(x As T, y As U)
End Sub
End Module
Module Main
Sub Test()
Dim s As String = "abc"
' Error: String does not satisfy the Structure constraint
s.M1(10)
' Error: T depends on U, which cannot be inferred
s.M2(10)
End Sub
End Module
注意。 执行扩展方法卷曲的主要原因之一是,它允许查询表达式在将参数计算为查询模式方法之前推断迭代的类型。 由于大多数查询模式方法采用 lambda 表达式,这需要类型推理本身,因此大大简化了计算查询表达式的过程。
与普通接口继承不同,扩展两个不相互关联的接口的扩展方法可用,前提是它们没有相同的 curried 签名:
Imports System.Runtime.CompilerServices
Interface I1
End Interface
Interface I2
End Interface
Class C1
Implements I1, I2
End Class
Module I1Ext
<Extension> _
Sub M1(i As I1, x As Integer)
End Sub
<Extension> _
Sub M2(i As I1, x As Integer)
End Sub
End Module
Module I2Ext
<Extension> _
Sub M1(i As I2, x As Integer)
End Sub
<Extension> _
Sub M2(I As I2, x As Double)
End Sub
End Module
Module Main
Sub Test()
Dim c As New C1()
' Error: M is ambiguous between I1Ext.M1 and I2Ext.M1.
c.M1(10)
' Calls I1Ext.M2
c.M2(10)
End Sub
End Module
最后,请务必记住,在执行后期绑定时,不会考虑扩展方法:
Module Test
Sub Main()
Dim o As Object = ...
' Ignores extension methods
o.M1()
End Sub
End Module
字典成员访问表达式
字典成员访问表达式用于查找集合的成员。 字典成员访问采用以下 E!I形式:其中 E ,表达式被分类为值,并且 I 是标识符。
DictionaryAccessExpression
: Expression? '!' IdentifierOrKeyword
;
表达式的类型必须具有由单个 String 参数编制索引的默认属性。 字典成员访问表达式
Class Keys
Public ReadOnly Default Property Item(s As String) As Integer
Get
Return 10
End Get
End Property
End Class
Module Test
Sub Main()
Dim x As Keys = new Keys()
Dim y As Integer
' The two statements are equivalent.
y = x!abc
y = x("abc")
End Sub
End Module
如果指定了没有表达式的感叹号,则假定立即包含 With 语句中的表达式。 如果没有包含 With 语句,则会发生编译时错误。
调用表达式
调用表达式由调用目标和可选参数列表组成。
InvocationExpression
: Expression ( OpenParenthesis ArgumentList? CloseParenthesis )?
;
ArgumentList
: PositionalArgumentList
| PositionalArgumentList Comma NamedArgumentList
| NamedArgumentList
;
PositionalArgumentList
: Expression? ( Comma Expression? )*
;
NamedArgumentList
: IdentifierOrKeyword ColonEquals Expression
( Comma IdentifierOrKeyword ColonEquals Expression )*
;
目标表达式必须分类为方法组或类型为委托类型的值。 如果目标表达式是一个类型为委托类型的值,则调用表达式的目标将成为委托类型的成员的方法组 Invoke ,而目标表达式将成为方法组的关联目标表达式。
参数列表有两个部分:位置参数和命名参数。
位置参数 是表达式,必须在任何命名参数之前。
命名参数 以可以匹配关键字的标识符开头,后跟 := 一个表达式。
如果方法组仅包含一个可访问的方法(包括实例和扩展方法),并且该方法不采用任何参数并且是一个函数,则方法组将解释为具有空参数列表的调用表达式,并将结果用作具有提供参数列表的调用表达式的目标。 例如:
Class C1
Function M1() As Integer()
Return New Integer() { 1, 2, 3 }
End Sub
End Class
Module Test
Sub Main()
Dim c As New C1()
' Prints 3
Console.WriteLine(c.M1(2))
End Sub
End Module
否则,重载解析将应用于方法,以便为给定参数列表(s)选取最适用的方法。 如果最适用的方法是函数,则调用表达式的结果被归类为类型化为函数的返回类型的值。 如果最适用的方法是子例程,则结果被归类为 void。 如果最适用的方法是没有正文的分部方法,则忽略调用表达式,并将结果归类为 void。
对于早期绑定调用表达式,参数的计算顺序是在目标方法中声明相应的参数的顺序。 对于后期绑定成员访问表达式,它们按成员访问表达式中显示的顺序进行计算:请参阅 Section Late-Bound 表达式。
重载的方法解析:
对于重载解析,给定参数列表、泛型、参数列表适用性、传递参数以及为可选参数、条件方法和类型参数推理选取参数的特定性:请参阅节 重载解析。
索引表达式
索引表达式导致数组元素或将属性组重新分类到属性访问中。 索引表达式由一个表达式、一个左括号、一个索引参数列表和一个右括号组成。
IndexExpression
: Expression OpenParenthesis ArgumentList? CloseParenthesis
;
索引表达式的目标必须分类为属性组或值。 索引表达式的处理方式如下:
如果目标表达式被分类为值,并且其类型不是数组类型,
Object或者System.Array,该类型必须具有默认属性。 对表示类型的所有默认属性的属性组执行索引。 尽管在 Visual Basic 中声明无参数默认属性无效,但其他语言可能允许声明此类属性。 因此,不允许为不带参数的属性编制索引。如果表达式导致数组类型的值,则参数列表中的参数数必须与数组类型的排名相同,并且不能包含命名参数。 如果任何索引在运行时无效,
System.IndexOutOfRangeException则会引发异常。 每个表达式必须隐式转换为类型Integer。 索引表达式的结果是指定索引处的变量,并归类为变量。如果表达式被归类为属性组,则重载解析用于确定其中一个属性是否适用于索引参数列表。 如果属性组仅包含一个
Get具有访问器的属性,如果该访问器不采用任何参数,则属性组将解释为具有空参数列表的索引表达式。 结果用作当前索引表达式的目标。 如果没有任何属性适用,则会发生编译时错误。 否则,该表达式将生成具有属性组关联目标表达式(如果有)的属性访问。如果表达式被归类为后期绑定属性组或类型为
Object或System.Array的值,则索引表达式的处理将延迟到运行时,并且索引是后期绑定的。 表达式导致后期绑定属性访问类型为Object. 关联的目标表达式是目标表达式(如果它是值)或属性组的关联目标表达式。 在运行时,表达式的处理方式如下:如果表达式被归类为后期绑定属性组,则表达式可能会导致方法组、属性组或值(如果成员是实例或共享变量)。 如果结果是方法组或属性组,则重载解析将应用于该组,以确定参数列表的正确方法。 如果重载解析失败,
System.Reflection.AmbiguousMatchException则会引发异常。 然后,将结果作为属性访问或调用进行处理,并返回结果。 如果调用是子例程,则结果是Nothing。如果目标表达式的运行时类型为数组类型,或
System.Array索引表达式的结果是指定索引处变量的值。否则,表达式的运行时类型必须具有默认属性,并且对表示类型上所有默认属性的属性组执行索引。 如果类型没有默认属性,则会
System.MissingMemberException引发异常。
新表达式
New 运算符用于创建新类型的实例。 有四种 New 形式的表达式:
对象创建表达式用于创建新的类类型和值类型的实例。
数组创建表达式用于创建新数组类型的实例。
委托创建表达式(没有与对象创建表达式不同的语法)用于创建新委托类型的实例。
匿名对象创建表达式用于创建匿名类类型的新实例。
NewExpression
: ObjectCreationExpression
| ArrayExpression
| AnonymousObjectCreationExpression
;
New表达式被分类为值,结果为类型的新实例。
Object-Creation 表达式
对象创建表达式用于创建类类型或结构类型的新实例。
ObjectCreationExpression
: 'New' NonArrayTypeName ( OpenParenthesis ArgumentList? CloseParenthesis )?
ObjectCreationExpressionInitializer?
;
ObjectCreationExpressionInitializer
: ObjectMemberInitializer
| ObjectCollectionInitializer
;
ObjectMemberInitializer
: 'With' OpenCurlyBrace FieldInitializerList CloseCurlyBrace
;
FieldInitializerList
: FieldInitializer ( Comma FieldInitializer )*
;
FieldInitializer
: 'Key'? ('.' IdentifierOrKeyword Equals )? Expression
;
ObjectCollectionInitializer
: 'From' CollectionInitializer
;
CollectionInitializer
: OpenCurlyBrace CollectionElementList? CloseCurlyBrace
;
CollectionElementList
: CollectionElement ( Comma CollectionElement )*
;
CollectionElement
: Expression
| CollectionInitializer
;
对象创建表达式的类型必须是类类型、结构类型或具有 New 约束的类型参数,不能是类 MustInherit 。 给定窗体 New T(A)的对象创建表达式(其中 T 类类型或结构类型是 A 可选参数列表),重载解析确定要调用的正确构造函数 T 。 具有 New 约束的类型参数被视为具有单个无参数构造函数。 如果没有可调用构造函数,则会发生编译时错误;否则,表达式将导致创建使用所选构造函数的新实例 T 。 如果没有参数,可以省略括号。
分配实例的位置取决于实例是类类型还是值类型。
New 类类型的实例是在系统堆上创建的,而值类型的新实例直接在堆栈上创建。
对象创建表达式可以选择在构造函数参数后指定成员初始值设定项的列表。 这些成员初始值设定项以关键字 With为前缀,初始值设定项列表被解释为在语句上下文 With 中。 例如,给定类:
Class Customer
Dim Name As String
Dim Address As String
End Class
代码:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = "123 Main St." }
End Sub
End Module
大致等效于:
Module Test
Sub Main()
Dim x, _t1 As Customer
_t1 = New Customer()
With _t1
.Name = "Bob Smith"
.Address = "123 Main St."
End With
x = _t1
End Sub
End Module
每个初始值设定项必须指定一个要分配的名称,并且该名称必须是正在构造的类型的非ReadOnly 实例变量或属性;如果构造的类型为 Object,则成员访问不会延迟绑定。 初始值设定项可能不使用 Key 关键字。 类型中的每个成员只能初始化一次。 但是,初始值设定项表达式可能相互引用。 例如:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = .Name & " St." }
End Sub
End Module
初始值设定项从左到右分配,因此,如果初始值设定项引用尚未初始化的成员,它将在构造函数运行后看到实例变量的任何值:
Module Test
Sub Main()
' The value of Address will be " St." since Name has not been
' assigned yet.
Dim x As New Customer() With { .Address = .Name & " St." }
End Sub
End Module
初始值设定项可以嵌套:
Class Customer
Dim Name As String
Dim Address As Address
Dim Age As Integer
End Class
Class Address
Dim Street As String
Dim City As String
Dim State As String
Dim ZIP As String
End Class
Module Test
Sub Main()
Dim c As New Customer() With { _
.Name = "John Smith", _
.Address = New Address() With { _
.Street = "23 Main St.", _
.City = "Peoria", _
.State = "IL", _
.ZIP = "13934" }, _
.Age = 34 }
End Sub
End Module
如果创建的类型是集合类型,并且具有一个名为 Add (包括扩展方法和共享方法)的实例方法,则对象创建表达式可以指定以关键字 From为前缀的集合初始值设定项。 对象创建表达式不能同时指定成员初始值设定项和集合初始值设定项。 集合初始值设定项中的每个元素作为参数传递给函数的 Add 调用。 例如:
Dim list = New List(Of Integer)() From { 1, 2, 3, 4 }
等效于:
Dim list = New List(Of Integer)()
list.Add(1)
list.Add(2)
list.Add(3)
如果元素是集合初始值设定项本身,则子集合初始值设定项的每个元素都将作为单个参数 Add 传递给函数。 例如,以下各项:
Dim dict = Dictionary(Of Integer, String) From { { 1, "One" },{ 2, "Two" } }
等效于:
Dim dict = New Dictionary(Of Integer, String)
dict.Add(1, "One")
dict.Add(2, "Two")
此扩展始终完成,并且只执行过一个级别深度;之后,子初始值设定项被视为数组文本。 例如:
' Error: List(Of T) does not have an Add method that takes two parameters.
Dim list = New List(Of Integer())() From { { 1, 2 }, { 3, 4 } }
' OK, this initializes the dictionary with (Integer, Integer()) pairs.
Dim dict = New Dictionary(Of Integer, Integer())() From _
{ { 1, { 2, 3 } }, { 3, { 4, 5 } } }
数组表达式
数组表达式用于创建数组类型的新实例。 有两种类型的数组表达式:数组创建表达式和数组文本。
数组创建表达式
如果提供了数组大小初始化修饰符,则通过从数组大小初始化参数列表中删除每个各个参数来派生生成的数组类型。 每个参数的值决定了新分配的数组实例中相应维度的上限。 如果表达式具有非空集合初始值设定项,则参数列表中的每个参数都必须为常量,表达式列表指定的排名和维度长度必须与集合初始值设定项的长度匹配。
Dim a() As Integer = New Integer(2) {}
Dim b() As Integer = New Integer(2) { 1, 2, 3 }
Dim c(,) As Integer = New Integer(1, 2) { { 1, 2, 3 } , { 4, 5, 6 } }
' Error, length/initializer mismatch.
Dim d() As Integer = New Integer(2) { 0, 1, 2, 3 }
如果未提供数组大小初始化修饰符,则类型名称必须是数组类型,并且集合初始值设定项必须为空,或者具有与指定数组类型的排名相同的嵌套级别。 最内部嵌套级别中的所有元素都必须隐式转换为数组的元素类型,并且必须归类为值。 每个嵌套集合初始值设定项中的元素数必须始终与同一级别的其他集合的大小一致。 从集合初始值设定项的每个相应嵌套级别中的元素数推断各个维度长度。 如果集合初始值设定项为空,则每个维度的长度为零。
Dim e() As Integer = New Integer() { 1, 2, 3 }
Dim f(,) As Integer = New Integer(,) { { 1, 2, 3 } , { 4, 5, 6 } }
' Error: Inconsistent numbers of elements!
Dim g(,) As Integer = New Integer(,) { { 1, 2 }, { 4, 5, 6 } }
' Error: Inconsistent levels of nesting!
Dim h(,) As Integer = New Integer(,) { 1, 2, { 3, 4 } }
集合初始值设定项的最外层嵌套级别对应于数组最左侧的维度,最内层的嵌套级别对应于最右侧的维度。 示例:
Dim array As Integer(,) = _
{ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 } }
等效于以下内容:
Dim array(4, 1) As Integer
array(0, 0) = 0: array(0, 1) = 1
array(1, 0) = 2: array(1, 1) = 3
array(2, 0) = 4: array(2, 1) = 5
array(3, 0) = 6: array(3, 1) = 7
array(4, 0) = 8: array(4, 1) = 9
如果集合初始值设定项为空(即包含大括号但不包含初始值设定项列表)和要初始化的数组维度的边界已知,则空集合初始值设定项表示指定大小的数组实例,其中所有元素都已初始化为元素类型的默认值。 如果初始化数组的维度的边界未知,空集合初始值设定项表示一个数组实例,其中所有维度的大小均为零。
数组实例的每个维度的排名和长度是实例整个生存期的常量。 换句话说,无法更改现有数组实例的排名,也不能调整其维度的大小。
数组文本
数组文本表示从表达式上下文和集合初始值设定项的组合推断其元素类型、排名和边界的数组。 本节 表达式重新分类对此进行了说明。
ArrayExpression
: ArrayCreationExpression
| ArrayLiteralExpression
;
ArrayCreationExpression
: 'New' NonArrayTypeName ArrayNameModifier CollectionInitializer
;
ArrayLiteralExpression
: CollectionInitializer
;
例如:
' array of integers
Dim a = {1, 2, 3}
' array of shorts
Dim b = {1S, 2S, 3S}
' array of shorts whose type is taken from the context
Dim c As Short() = {1, 2, 3}
' array of type Integer(,)
Dim d = {{1, 0}, {0, 1}}
' jagged array of rank ()()
Dim e = {({1, 0}), ({0, 1})}
' error: inconsistent rank
Dim f = {{1}, {2, 3}}
' error: inconsistent rank
Dim g = {1, {2}}
数组文本中集合初始值设定项的格式和要求与数组创建表达式中的集合初始值设定项的格式和要求完全相同。
注意。 数组文本本身不会创建数组;而是将表达式重新分类为导致创建数组的值。 例如,转换是不可能的,因为没有从Integer()该转换CType(new Integer() {1,2,3}, Short())到 Short();但表达式CType({1,2,3},Short())是可能的,因为它首先将数组文本重新分类到数组创建表达式New Short() {1,2,3}中。
Delegate-Creation 表达式
委托创建表达式用于创建委托类型的新实例。 委托创建表达式的参数必须是分类为方法指针或 lambda 方法的表达式。
如果参数是方法指针,则方法指针引用的方法之一必须适用于委托类型的签名。
M
D如果:
M不是Partial或有正文。D既M是函数,又D是子例程。M并D具有相同数量的参数。每个参数类型的参数类型
M具有从相应参数类型的D转换,其修饰符(即ByRef)ByVal匹配。返回类型
M(如果有)具有转换为返回类型的返回类型D。
如果方法指针引用后期绑定访问,则后期绑定访问假定为与委托类型具有相同数量的参数的函数。
如果未使用严格的语义,并且方法指针只引用了一个方法,但由于没有参数且委托类型无效,则该方法被视为适用且参数或返回值将被忽略。 例如:
Delegate Sub F(x As Integer)
Module Test
Sub M()
End Sub
Sub Main()
' Valid
Dim x As F = AddressOf M
End Sub
End Module
注意。 仅当由于扩展方法而不使用严格的语义时,才允许这种放松。 由于仅当常规方法不适用时,才考虑扩展方法,因此没有参数的实例方法可以隐藏具有参数的扩展方法,以便进行委托构造。
如果方法指针引用的多个方法适用于委托类型,则重载解析用于在候选方法之间进行选取。 委托的参数类型用作重载解析的参数类型。 如果没有一个候选方法最适用,则会发生编译时错误。 在下面的示例中,局部变量使用引用第二 Square 个方法的委托进行初始化,因为该方法更适用于签名和返回类型 DoubleFunc。
Delegate Function DoubleFunc(x As Double) As Double
Module Test
Function Square(x As Single) As Single
Return x * x
End Function
Function Square(x As Double) As Double
Return x * x
End Function
Sub Main()
Dim a As New DoubleFunc(AddressOf Square)
End Sub
End Module
如果没有第二 Square 种方法,则会选择第一 Square 个方法。 如果编译环境或编译 Option Strict环境指定了严格的语义,则当方法指针引用的最特定方法比委托签名窄时,会发生编译时错误。 如果以下项,则方法 M 被视为比委托类型 D 窄:
参数类型
M具有对相应参数类型的D扩大转换。或者,返回类型(如果有)
M对返回类型的D转换范围缩小。
如果类型参数与方法指针相关联,则只考虑具有相同类型参数数的方法。 如果没有与方法指针关联的类型参数,则当将签名与泛型方法匹配时,将使用类型推理。 与其他普通类型推理不同,在推断类型参数时使用委托的返回类型,但在确定最小泛型重载时仍不考虑返回类型。 以下示例演示向委托创建表达式提供类型参数的两种方式:
Delegate Function D(s As String, i As Integer) As Integer
Delegate Function E() As Integer
Module Test
Public Function F(Of T)(s As String, t1 As T) As T
End Function
Public Function G(Of T)() As T
End Function
Sub Main()
Dim d1 As D = AddressOf f(Of Integer) ' OK, type arg explicit
Dim d2 As D = AddressOf f ' OK, type arg inferred
Dim e1 As E = AddressOf g(Of Integer) ' OK, type arg explicit
Dim e2 As E = AddressOf g ' OK, infer from return
End Sub
End Module
在上面的示例中,使用泛型方法实例化非泛型委托类型。 还可以使用泛型方法创建构造委托类型的实例。 例如:
Delegate Function Predicate(Of U)(u1 As U, u2 As U) As Boolean
Module Test
Function Compare(Of T)(t1 As List(of T), t2 As List(of T)) As Boolean
...
End Function
Sub Main()
Dim p As Predicate(Of List(Of Integer))
p = AddressOf Compare(Of Integer)
End Sub
End Module
如果委托创建表达式的参数是 lambda 方法,则 lambda 方法必须适用于委托类型的签名。 如果以下情况下,lambda 方法 L 适用于委托类型 D :
如果
L具有参数,D则具有相同数量的参数。 (如果没有L参数,则忽略参数D。每个参数类型的参数类型
L都转换为相应的参数类型D,其修饰符(即ByRef)ByVal匹配。如果
D为函数,则返回类型L具有转换为返回类型的返回类型D。 (如果D为子例程,则忽略返回L值。
如果省略参数的参数 L 类型,则会推断相应的参数 D 的类型;如果 L 参数具有数组或可为 null 的名称修饰符,则编译时错误结果。 所有参数类型 L 都可用后,将推断 lambda 方法中的表达式类型。 例如:
Delegate Function F(x As Integer, y As Long) As Long
Module Test
Sub Main()
' b inferred to Integer, c and return type inferred to Long
Dim a As F = Function(b, c) b + c
' e and return type inferred to Integer, f inferred to Long
Dim d As F = Function(e, f) e + CInt(f)
End Sub
End Module
在某些情况下,委托签名与 lambda 方法或方法签名不完全匹配,.NET Framework 可能不支持本机创建委托。 在这种情况下,lambda 方法表达式用于匹配这两种方法。 例如:
Delegate Function IntFunc(x As Integer) As Integer
Module Test
Function SquareString(x As String) As String
Return CInt(x) * CInt(x)
End Function
Sub Main()
' The following two lines are equivalent
Dim a As New IntFunc(AddressOf SquareString)
Dim b As New IntFunc( _
Function(x As Integer) CInt(SquareString(CStr(x))))
End Sub
End Module
委托创建表达式的结果是一个委托实例,它引用与方法指针表达式中关联的目标表达式(如果有)的匹配方法。 如果目标表达式类型为值类型,则值类型将复制到系统堆上,因为委托只能指向堆上对象的方法。 委托引用的方法和对象在委托的整个生存期内保持不变。 换句话说,创建委托后无法更改该委托的目标或对象。
匿名 Object-Creation 表达式
具有成员初始值设定项的对象创建表达式也可以完全省略类型名称。
AnonymousObjectCreationExpression
: 'New' ObjectMemberInitializer
;
在这种情况下,将基于作为表达式一部分初始化的成员的类型和名称构造匿名类型。 例如:
Module Test
Sub Main()
Dim Customer = New With { .Name = "John Smith", .Age = 34 }
Console.WriteLine(Customer.Name)
End Sub
End Module
匿名对象创建表达式创建的类型是一个类,该类没有名称,直接继承自 Object,并且具有与成员初始值设定项列表中分配给的成员相同的一组属性。 使用与局部变量类型推理相同的规则推断每个属性的类型。 生成的匿名类型也会重写 ToString,并返回所有成员及其值的字符串表示形式。 (此字符串的确切格式超出了此规范的范围)。
默认情况下,匿名类型生成的属性为读写。 可以使用修饰符将匿名类型属性标记为只读 Key 。
Key修饰符指定字段可用于唯一标识匿名类型表示的值。 除了使属性只读,它还会导致匿名类型重写Equals并GetHashCode实现接口System.IEquatable(Of T)(填写匿名类型)。T 成员的定义如下:
Function Equals(obj As Object) As Boolean 并通过 Function Equals(val As T) As Boolean 验证两个实例的类型相同,然后使用每个 Key 成员 Object.Equals进行比较来实现。 如果所有 Key 成员都相等,则 Equals 返回 True,否则 Equals 返回 False。
Function GetHashCode() As Integer 实现后,如果 Equals 匿名类型的两个实例为 true,则 GetHashCode 返回相同的值。 该哈希以种子值开头,然后,每个Key成员的顺序将哈希乘以 31Key,如果成员不是引用类型或可以为 null 的值类型,则添加成员的哈希值(提供GetHashCode者)。Nothing
例如,在语句中创建的类型:
Dim zipState = New With { Key .ZipCode = 98112, .State = "WA" }
创建一个类似如下的类(尽管确切的实现可能有所不同):
Friend NotInheritable Class $Anonymous1
Implements IEquatable(Of $Anonymous1)
Private ReadOnly _zipCode As Integer
Private _state As String
Public Sub New(zipCode As Integer, state As String)
_zipCode = zipcode
_state = state
End Sub
Public ReadOnly Property ZipCode As Integer
Get
Return _zipCode
End Get
End Property
Public Property State As String
Get
Return _state
End Get
Set (value As Integer)
_state = value
End Set
End Property
Public Overrides Function Equals(obj As Object) As Boolean
Dim val As $Anonymous1 = TryCast(obj, $Anonymous1)
Return Equals(val)
End Function
Public Overloads Function Equals(val As $Anonymous1) As Boolean _
Implements IEquatable(Of $Anonymous1).Equals
If val Is Nothing Then
Return False
End If
If Not Object.Equals(_zipCode, val._zipCode) Then
Return False
End If
Return True
End Function
Public Overrides Function GetHashCode() As Integer
Dim hash As Integer = 0
hash = hash Xor _zipCode.GetHashCode()
Return hash
End Function
Public Overrides Function ToString() As String
Return "{ Key .ZipCode = " & _zipCode & ", .State = " & _state & " }"
End Function
End Class
为了简化从另一种类型的字段创建匿名类型的情况,可以直接从表达式推断字段名称,在这种情况下:
简单名称表达式
x推断名称x。成员访问表达式
x.y推断名称y。字典查找表达式
x!y推断名称y。没有参数
x()的调用或索引表达式推断名称x。XML 成员访问表达式
x.<y>,x...<y>x.@y推断名称y。作为成员访问表达式目标的 XML 成员访问表达式
x.<y>.z推断名称z。XML 成员访问表达式,该表达式是调用或索引表达式的目标,没有参数
x.<y>.z()推断出名称z。作为调用或索引表达式的目标的 XML 成员访问表达式
x.<y>(0)推断名称y。
初始值设定项被解释为将表达式赋给推断的名称。 例如,以下初始值设定项等效:
Class Address
Public Street As String
Public City As String
Public State As String
Public ZIP As String
End Class
Class C1
Sub Test(a As Address)
Dim cityState1 = New With { .City = a.City, .State = a.State }
Dim cityState2 = New With { a.City, a.State }
End Sub
End Class
如果推断出与类型现有成员冲突的成员名称,例如 GetHashCode,则会发生编译时错误。 与常规成员初始值设定项不同,匿名对象创建表达式不允许成员初始值设定项具有循环引用,也不允许在初始化成员之前引用成员。 例如:
Module Test
Sub Main()
' Error: Circular references
Dim x = New With { .a = .b, .b = .a }
' Error: Referring to .b before it has been assigned to
Dim y = New With { .a = .b, .b = 10 }
' Error: Referring to .a before it has been assigned to
Dim z = New With { .a = .a }
End Sub
End Module
如果两个匿名类创建表达式在同一方法中出现,并生成相同的结果形状 -- 如果属性顺序、属性名称和属性类型都匹配 -- 它们都将引用同一匿名类。 具有初始值设定项的实例或共享成员变量的方法范围是初始化变量的构造函数。
注意。 编译器可能选择进一步统一匿名类型,例如在程序集级别,但目前不能依赖这一点。
强制转换表达式
强制转换表达式将表达式强制转换为给定类型。 特定的强制转换关键字将表达式强制转换为基元类型。 三个常规强制转换关键字,CTypeTryCast以及DirectCast将表达式强制转换为类型。
CastExpression
: 'DirectCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
| 'TryCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
| 'CType' OpenParenthesis Expression Comma TypeName CloseParenthesis
| CastTarget OpenParenthesis Expression CloseParenthesis
;
CastTarget
: 'CBool' | 'CByte' | 'CChar' | 'CDate' | 'CDec' | 'CDbl' | 'CInt'
| 'CLng' | 'CObj' | 'CSByte' | 'CShort' | 'CSng' | 'CStr' | 'CUInt'
| 'CULng' | 'CUShort'
;
DirectCast 具有 TryCast 特殊行为。 因此,它们仅支持本机转换。 此外,表达式中的 TryCast 目标类型不能是值类型。 用户定义转换运算符在使用或TryCast使用时DirectCast不考虑。 (注意。 转换集 DirectCast 和 TryCast 支持受到限制,因为它们实现了“本机 CLR”转换。目的是 DirectCast 提供“取消装箱”指令的功能,而目的是 TryCast 提供“isinst”指令的功能。由于它们映射到 CLR 指令,因此不支持 CLR 直接支持的转换会破坏预期目的。
DirectCast 将类型化为 Object 与 CType.. 不同的表达式进行转换。 转换其运行时类型Object为基元值类型的表达式时,DirectCast如果指定的类型与表达式的运行时类型不同,或者System.NullReferenceException表达式的计算结果Nothing为 ,则引发System.InvalidCastException异常。 (注意。如上所述,当表达式的类型为Object“取消装箱”时,DirectCast直接映射到 CLR 指令。相反,CType转换为对运行时帮助程序进行转换的调用,以便支持基元类型之间的转换。在将表达式转换为基元值类型且实际实例的类型与目标类型匹配时Object,DirectCast其速度将明显快于 CType.)
TryCast 转换表达式,但如果表达式无法转换为目标类型,则不会引发异常。 相反,如果在运行时无法转换表达式, TryCast 则会导致 Nothing 该表达式。 (注意。 如上所述, TryCast 直接映射到 CLR 指令“isinst”。通过将类型检查和转换合并为单个作, TryCast 可以比执行 TypeOf ... Is 一个作和一个 CType作便宜。
例如:
Interface ITest
Sub Test()
End Interface
Module Test
Sub Convert(o As Object)
Dim i As ITest = TryCast(o, ITest)
If i IsNot Nothing Then
i.Test()
End If
End Sub
End Module
如果不存在从表达式类型到指定类型的转换,则会发生编译时错误。 否则,表达式被分类为值,结果为转换生成的值。
运算符表达式
有两种类型的运算符。
一元运算符 采用一个作数并使用前缀表示法(例如, -x)。
二进制运算符 采用两个作数并使用不合符表示法(例如, x + y)。 除了始终导致 Boolean的关系运算符外,为特定类型定义的运算符会导致该类型。 运算符的作数必须始终分类为值;运算符表达式的结果被分类为值。
OperatorExpression
: ArithmeticOperatorExpression
| RelationalOperatorExpression
| LikeOperatorExpression
| ConcatenationOperatorExpression
| ShortCircuitLogicalOperatorExpression
| LogicalOperatorExpression
| ShiftOperatorExpression
| AwaitOperatorExpression
;
运算符优先级和结合性
当表达式包含多个二进制运算符时,运算符的 优先级 控制计算单个二进制运算符的顺序。 例如,表达式 x + y * z 的计算方式 x + (y * z) 为,因为 * 运算符的优先级高于 + 运算符。 下表列出了按优先级降序排列的二进制运算符:
| 类别 | 操作员 |
|---|---|
| 主要 | 所有非运算符表达式 |
| 等待 | Await |
| 求幂 | ^ |
| 一元求反 |
+、- |
| 乘法性的 |
*、/ |
| 整数除法 | \ |
| 模数 | Mod |
| 累加性 |
+、- |
| 串联 | & |
| 转变 |
<<、>> |
| 关系 |
=、<>、<、>、<=、>=、Like、Is、IsNot |
| 逻辑 NOT | Not |
| 逻辑与 |
And、AndAlso |
| 逻辑或 |
Or、OrElse |
| 逻辑“异或” | Xor |
当表达式包含具有相同优先级的两个运算符时,运算符的 关联性 控制执行作的顺序。 所有二进制运算符都是左关联运算符,这意味着从左到右执行作。 可以使用括号表达式控制优先级和关联性。
对象作数
除了每个运算符支持的常规类型外,所有运算符都支持类型的 Object作数。 应用于 Object 作数的运算符的处理方式与对 Object 值进行的方法调用类似:可以选择后期绑定方法调用,在这种情况下,作数的运行时类型(而不是编译时类型)决定了作的有效性和类型。 如果编译环境指定了严格的语义,则具有类型Object作数的任何运算符都会导致编译时错误(除编译时错误和IsNot运算符除外TypeOf...IsIs)。Option Strict
当运算符解析确定应延迟执行作时,如果作数的运行时类型是运算符支持的类型,则作的结果是将运算符应用于作数类型的结果。
Nothing该值被视为二进制运算符表达式中其他作数类型的默认值。 在一元运算符表达式中,或者如果两个作数都位于 Nothing 二元运算符表达式中,则运算的类型或 Integer 运算符的唯一结果类型(如果运算符不产生 Integer)。 然后始终将作的结果转换回 Object。 如果作数类型没有有效的运算符, System.InvalidCastException 则会引发异常。 运行时的转换不考虑它们是隐式转换还是显式转换。
如果数值二进制运算的结果将产生溢出异常(无论整数溢出检查是打开还是关闭),则结果类型将提升为下一个更广泛的数值类型(如果可能)。 例如,考虑以下代码:
Module Test
Sub Main()
Dim o As Object = CObj(CByte(2)) * CObj(CByte(255))
Console.WriteLine(o.GetType().ToString() & " = " & o)
End Sub
End Module
它输出以下结果:
System.Int16 = 512
如果没有更广泛的数值类型可用于保存数字, System.OverflowException 则会引发异常。
运算符解析
给定运算符类型和一组作数,运算符解析确定要用于作数的运算符。 解析运算符时,首先会考虑用户定义的运算符,使用以下步骤:
首先,收集所有候选运算符。 候选运算符是源类型中特定运算符类型的用户定义运算符以及目标类型中特定类型的所有用户定义的运算符。 如果源类型和目标类型相关,则通用运算符仅被视为一次。
然后,重载解析应用于运算符和作数,以选择最具体的运算符。 对于二进制运算符,这可能会导致延迟调用。
收集类型的 T?候选运算符时,将改用类型的 T 运算符。
T还将提升任何只涉及不可为 null 值类型的用户定义的运算符。 提升运算符使用任何值类型的可为 null 版本,但返回类型的返回类型 IsTrue 除外( IsFalse 必须为 Boolean)。 提升运算符通过将作数转换为不可为 null 的版本,然后评估用户定义的运算符,然后将结果类型转换为其可为 null 的版本来计算。 如果为以太网作数 Nothing,则表达式的结果是类型化为结果类型的可为 null 版本的值 Nothing 。 例如:
Structure T
...
End Structure
Structure S
Public Shared Operator +(ByVal op1 As S, ByVal op2 As T) As T
...
End Operator
End Structure
Module Test
Sub Main()
Dim x As S?
Dim y, z As T?
' Valid, as S + T = T is lifted to S? + T? = T?
z = x + y
End Sub
End Module
如果运算符是二进制运算符,其中一个作数是引用类型,则也会提升该运算符,但对运算符的任何绑定都会产生错误。 例如:
Structure S1
Public F1 As Integer
Public Shared Operator +(left As S1, right As String) As S1
...
End Operator
End Structure
Module Test
Sub Main()
Dim a? As S1
Dim s As String
' Error: '+' is not defined for S1? and String
a = a + s
End Sub
End Module
注意。 之所以存在此规则,是因为我们考虑是否希望在将来的版本中添加 null 传播引用类型,在这种情况下,在两种类型之间二元运算符的行为将发生更改。
与转换一样,用户定义运算符始终优先于提升运算符。
解析重载运算符时,Visual Basic 中定义的类和其他语言中定义的类之间可能存在差异:
在其他语言中
Not,And也可以Or作为逻辑运算符和按位运算符重载。 从外部程序集导入后,接受任一窗体作为这些运算符的有效重载。 但是,对于同时定义逻辑运算符和按位运算符的类型,只考虑按位实现。在其他语言中,
>><<可以同时作为已签名运算符和未签名运算符重载。 从外部程序集导入后,将任一窗体接受为有效的重载。 但是,对于定义已签名运算符和未签名运算符的类型,将仅考虑已签名实现。如果没有用户定义的运算符最特定于作数,则将考虑内部运算符。 如果未为作数定义任何内部运算符,并且任一作数都具有 Object 类型,则将后期解析该运算符;否则,将生成编译时错误。
在 Visual Basic 的早期版本中,如果对象类型正好有一个作数,并且没有适用的用户定义的运算符,并且没有适用的内部运算符,则这是一个错误。 从 Visual Basic 11 开始,它现已解析为后期绑定。 例如:
Module Module1
Sub Main()
Dim p As Object = Nothing
Dim U As New Uri("http://www.microsoft.com")
Dim j = U * p ' is now resolved late-bound
End Sub
End Module
具有内部运算符的类型 T 还定义了相同的运算符 T?。 运算符打开? 将从具有作的任意作数中删除作数,确定作的类型,如果任何作数是可以为 null 的值类型,则会将作 ? 类型添加到该作的类型。 例如:
Dim v1? As Integer = 10
Dim v2 As Long = 20
' Type of operation will be Long?
Console.WriteLine(v1 + v2)
每个运算符列出了为其定义的固有类型,以及给定作数类型执行的作的类型。 内部作类型的结果遵循以下常规规则:
如果所有作数都属于同一类型,并且为类型定义了运算符,则不会发生转换,并且使用该类型的运算符。
未为运算符定义类型的任何作数都使用以下步骤进行转换,并且针对新类型解析该运算符:
作数将转换为为运算符和作数定义的下一个最宽的类型,以及该作数可隐式转换为该类型。
如果没有此类类型,则作数将转换为为运算符和作数定义的下一个最窄的类型,并将其隐式转换为该作数。
如果没有此类类型或无法进行转换,则会发生编译时错误。
否则,作数将转换为更广泛的作数类型和使用该类型的运算符。 如果较窄的作数类型无法隐式转换为更广泛的运算符类型,则会发生编译时错误。
然而,尽管这些常规规则在运算符结果表中列出了许多特殊情况。
注意。 出于格式设置原因,运算符类型表将预定义名称缩写为前两个字符。 因此,“By”是 Byte,“UI”是 UInteger,“St”是 String等,“Err”表示没有为给定作数类型定义的作。
算术运算符
*、/、\、^Mod、+和-运算符是算术运算符。
ArithmeticOperatorExpression
: UnaryPlusExpression
| UnaryMinusExpression
| AdditionOperatorExpression
| SubtractionOperatorExpression
| MultiplicationOperatorExpression
| DivisionOperatorExpression
| ModuloOperatorExpression
| ExponentOperatorExpression
;
浮点算术运算的精度可能高于运算的结果类型。 例如,某些硬件体系结构支持具有比该类型更大的范围和精度的“扩展”或“长双精度”浮点类型,并使用此更高精度 Double 类型隐式执行所有浮点作。 硬件体系结构可以执行浮点作,其精度较低,但性能成本过高:Visual Basic 允许对所有浮点作使用更高的精度类型,而不是要求实现来放弃性能和精度。 除了提供更精确的结果之外,这很少产生任何可衡量的效果。 但是,在窗体 x * y / z的表达式中,乘法生成超出 Double 范围的结果,但后续除法将临时结果带回 Double 范围,因此以较高范围格式计算表达式可能导致生成有限结果而不是无穷大。
一元加号运算符
UnaryPlusExpression
: '+' Expression
;
一元加运算符针对Byte类型定义、SByte、UShort、ULongUIntegerShortLongSingleIntegerDouble和Decimal类型。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
一元减号运算符
UnaryMinusExpression
: '-' Expression
;
为以下类型定义一元减号运算符:
SByte、Short、Integer 和 Long。 通过从零减去作数来计算结果。 如果整数溢出检查处于打开,并且作数的值是最大负SByte数,ShortInteger或LongSystem.OverflowException引发异常。 否则,如果作数的值是最大负SByte数、ShortInteger或Long结果为相同的值,并且不会报告溢出。
Single 和 Double。 结果是作数的值,其符号反转,包括值 0 和 Infinity。 如果作数为 NaN,则结果也是 NaN。
Decimal。 通过从零减去作数来计算结果。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
加法运算符
加法运算符计算两个作数的总和。
AdditionOperatorExpression
: Expression '+' LineTerminator? Expression
;
为以下类型定义加法运算符:
Byte、SByte、UShort、Short、UInteger、Integer、ULong和Long。 如果整数溢出检查处于打开,并且总和超出结果类型的范围,System.OverflowException则会引发异常。 否则,不会报告溢出,并且丢弃结果的任何重大高阶位。Single和Double。 根据 IEEE 754 算术规则计算总和。Decimal。 如果生成的值太大而无法以小数格式表示,System.OverflowException则会引发异常。 如果结果值太小,无法以十进制格式表示,则结果为 0。String。 两String个作数连接在一起。Date。 该System.DateTime类型定义重载加法运算符。 由于System.DateTime等效于内部Date类型,因此这些运算符也可用于该Date类型。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | Sh | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
| 某人 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |
| 发布者 | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||
| Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||
| 美国 | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||
| 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||
| 用户界面 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||
| 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||
| UL | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||
| 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||
| 四 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||||
| 建议做法 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||||
| 大 | 圣 | 犯 错 | 圣 | Ob | ||||||||||||
| Ch | 圣 | 圣 | Ob | |||||||||||||
| 圣 | 圣 | Ob | ||||||||||||||
| Ob | Ob |
减法运算符
减法运算符从第一个作数中减去第二个作数。
SubtractionOperatorExpression
: Expression '-' LineTerminator? Expression
;
为以下类型定义减法运算符:
Byte、SByte、UShort、Short、UInteger、Integer、ULong和Long。 如果整数溢出检查处于打开且差异超出结果类型的范围,System.OverflowException则会引发异常。 否则,不会报告溢出,并且丢弃结果的任何重大高阶位。Single和Double。 根据 IEEE 754 算术规则计算差异。Decimal。 如果生成的值太大而无法以小数格式表示,System.OverflowException则会引发异常。 如果结果值太小,无法以十进制格式表示,则结果为 0。Date。 该System.DateTime类型定义重载减法运算符。 由于System.DateTime等效于内部Date类型,因此这些运算符也可用于该Date类型。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | Sh | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
| 某人 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |
| 发布者 | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||
| Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||
| 美国 | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||
| 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||
| 用户界面 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||
| 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||
| UL | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||
| 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||
| 四 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||||
| 建议做法 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 去做 | Ob | ||||||||||||||
| Ob | Ob |
乘法运算符
乘法运算符计算两个作数的乘积。
MultiplicationOperatorExpression
: Expression '*' LineTerminator? Expression
;
为以下类型定义乘法运算符:
Byte、SByte、UShort、Short、UInteger、Integer、ULong和Long。 如果整数溢出检查处于打开且产品超出结果类型范围,System.OverflowException则会引发异常。 否则,不会报告溢出,并且丢弃结果的任何重大高阶位。Single和Double。 该产品是根据 IEEE 754 算术规则计算的。Decimal。 如果生成的值太大而无法以小数格式表示,System.OverflowException则会引发异常。 如果结果值太小,无法以十进制格式表示,则结果为 0。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | Sh | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
| 某人 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |
| 发布者 | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||
| Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||
| 美国 | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||
| 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||
| 用户界面 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||
| 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||
| UL | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||
| 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||
| 四 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||||
| 建议做法 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 去做 | Ob | ||||||||||||||
| Ob | Ob |
除法运算符
除法运算符计算两个作数的商。 有两个除法运算符:常规(浮点)除法运算符和整数除法运算符。
DivisionOperatorExpression
: FPDivisionOperatorExpression
| IntegerDivisionOperatorExpression
;
FPDivisionOperatorExpression
: Expression '/' LineTerminator? Expression
;
IntegerDivisionOperatorExpression
: Expression '\\' LineTerminator? Expression
;
为以下类型定义常规除法运算符:
Single和Double。 根据 IEEE 754 算术规则计算商。Decimal。 如果右作数的值为零,System.DivideByZeroException则会引发异常。 如果生成的值太大而无法以小数格式表示,System.OverflowException则会引发异常。 如果结果值太小,无法以十进制格式表示,则结果为零。 结果的刻度(在任何舍入之前)是最接近首选比例的刻度,它将保留结果等于确切结果。 首选刻度是第一个作数的刻度,小于第二个作数的刻度。
根据正常的运算符解析规则,常规除法在类型(如ByteShortInteger)的作数之间,并Long会导致两个作数转换为类型。Decimal 但是,当对除法运算符执行运算符解析时,这两种类型DecimalDouble都被视为小于 Decimal。 遵循此约定是因为 Double 除法比 Decimal 除法更有效。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
| 某人 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |
| 发布者 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||
| Sh | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||
| 美国 | 去做 | 去做 | 去做 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||
| 在 | 去做 | 去做 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||
| 用户界面 | 去做 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||
| 瞧 | 去做 | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||
| UL | 去做 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||
| 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||
| 四 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||||
| 建议做法 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 去做 | Ob | ||||||||||||||
| Ob | Ob |
整数除法运算符定义为 Byte、、SByte、ShortUShort、UInteger、 Integer和 ULongLong。 如果右作数的值为零, System.DivideByZeroException 则会引发异常。 除法将结果舍入为零,结果的绝对值是小于两个作数商的绝对值的最大可能整数。 当两个作数具有相同的符号时,结果为零或正,当两个作数具有相反的符号时,结果为零或负。 如果左侧作数是最大负 SByte数、 Short右 Integer作数或 Long右作数 -1,则会发生溢出;如果打开整数溢出检查, System.OverflowException 则会引发异常。 否则,不会报告溢出,结果是左作数的值。
注意。 由于无符号类型的两个作数始终为零或正,因此结果始终为零或正。 由于表达式的结果将始终小于或等于两个作数中最大的作数,因此无法发生溢出。 因此,不对具有两个无符号整数的整数除法执行整数溢出检查。 结果是左作数的类型。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | Sh | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob |
| 某人 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |
| 发布者 | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||
| Sh | Sh | 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||
| 美国 | 美国 | 在 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||
| 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||
| 用户界面 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||||
| 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||||
| UL | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||||||
| 德 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||||||
| 四 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||||||||
| 建议做法 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 瞧 | Ob | ||||||||||||||
| Ob | Ob |
Mod 运算符
Mod (modulo) 运算符计算两个作数之间的除数的余数。
ModuloOperatorExpression
: Expression 'Mod' LineTerminator? Expression
;
为 Mod 以下类型定义运算符:
Byte、、SByte、ShortUShort、UInteger、、ULongInteger和Long。x Mod y的结果是由x - (x \ y) * y生成的值。 如果y为零,System.DivideByZeroException则会引发异常。 modulo 运算符永远不会导致溢出。Single和Double。 余数根据 IEEE 754 算术规则计算。Decimal。 如果右作数的值为零,System.DivideByZeroException则会引发异常。 如果生成的值太大而无法以小数格式表示,System.OverflowException则会引发异常。 如果结果值太小,无法以十进制格式表示,则结果为零。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | Sh | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
| 某人 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |
| 发布者 | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||
| Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||
| 美国 | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||
| 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||
| 用户界面 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||
| 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||
| UL | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||
| 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||
| 四 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||||
| 建议做法 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 去做 | Ob | ||||||||||||||
| Ob | Ob |
指数运算符
指数运算符计算第一个作数提升到第二个作数的幂。
ExponentOperatorExpression
: Expression '^' LineTerminator? Expression
;
为类型 Double定义指数运算符。 该值根据 IEEE 754 算术规则计算。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob |
| 某人 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |
| 发布者 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||
| Sh | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||
| 美国 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||
| 在 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||
| 用户界面 | 去做 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||
| 瞧 | 去做 | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||
| UL | 去做 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||
| 德 | 去做 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||
| 四 | 去做 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||||
| 建议做法 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 去做 | Ob | ||||||||||||||
| Ob | Ob |
关系运算符
关系运算符将值与其他值进行比较。 比较运算符为 =、、<>、<>、 <=和 >=。
RelationalOperatorExpression
: Expression '=' LineTerminator? Expression
| Expression '<' '>' LineTerminator? Expression
| Expression '<' LineTerminator? Expression
| Expression '>' LineTerminator? Expression
| Expression '<' '=' LineTerminator? Expression
| Expression '>' '=' LineTerminator? Expression
;
所有关系运算符都会导致一个 Boolean 值。
关系运算符具有以下一般含义:
=运算符测试两个作数是否相等。<>运算符测试两个作数是否不相等。运算符
<测试第一个作数是否小于第二个作数。该
>运算符测试第一个作数是否大于第二个作数。运算符
<=测试第一个作数是否小于或等于第二个作数。运算符
>=测试第一个作数是否大于或等于第二个作数。
关系运算符为以下类型定义:
Boolean。 运算符比较两个作数的真值。True被视为小于False,它与其数值匹配。Byte、SByte、UShort、Short、UInteger、Integer、ULong和Long。 运算符比较两个整型作数的数值。Single和Double。 运算符根据 IEEE 754 标准的规则比较作数。Decimal。 运算符比较两个十进制作数的数值。Date。 运算符返回比较两个日期/时间值的结果。Char。 运算符返回比较两个 Unicode 值的结果。String。 运算符返回使用二进制比较或文本比较来比较两个值的结果。 使用的比较由编译环境和Option Compare语句决定。 二进制比较确定每个字符串中每个字符的数字 Unicode 值是否相同。 文本比较基于 .NET Framework 上使用的当前区域性执行 Unicode 文本比较。 执行字符串比较时,null 值等效于字符串文本""。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | 博 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 博 | Ob |
| 某人 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |
| 发布者 | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||
| Sh | Sh | 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||
| 美国 | 美国 | 在 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||
| 在 | 在 | 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||
| 用户界面 | UI | 瞧 | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||
| 瞧 | 瞧 | 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||
| UL | UL | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||
| 德 | 德 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||
| 四 | 四 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | ||||||||||
| 建议做法 | 去做 | 犯 错 | 犯 错 | 去做 | Ob | |||||||||||
| 大 | Da | 犯 错 | Da | Ob | ||||||||||||
| Ch | Ch | 圣 | Ob | |||||||||||||
| 圣 | 圣 | Ob | ||||||||||||||
| Ob | Ob |
Like 运算符
运算符 Like 确定字符串是否与给定模式匹配。
LikeOperatorExpression
: Expression 'Like' LineTerminator? Expression
;
为 Like 类型定义 String 运算符。 第一个作数是要匹配的字符串,第二个作数是要匹配的模式。 该模式由 Unicode 字符组成。 以下字符序列具有特殊含义:
该字符
?与任何单个字符匹配。该字符
*匹配零个或多个字符。该字符
#匹配任何单个数字(0-9)。括在括号(
[ab...])中的字符列表与列表中的任何单个字符匹配。用方括号括起来且以感叹号(
[!ab...])为前缀的字符列表与字符列表中没有的任何单个字符匹配。字符列表中的两个字符用连字符(
-)分隔,指定以第一个字符开头的 Unicode 字符范围,以第二个字符结尾。 如果第二个字符的排序顺序不晚于第一个字符,则会发生运行时异常。 字符列表开头或末尾显示的连字符指定自身。
若要匹配左括号()、问号([)、数字符号(?#)和星号(*)的特殊字符,括号必须括起来。 右括号 (]) 不能在组中用于匹配自身,但它可以在组外部用作单个字符。 字符序列 [] 被视为字符串文本 ""。
请注意,字符列表的字符比较和排序取决于所使用的比较类型。 如果使用二进制比较,字符比较和排序基于数字 Unicode 值。 如果使用文本比较,字符比较和排序基于 .NET Framework 上使用的当前区域设置。
在某些语言中,字母表中的特殊字符表示两个单独的字符,反之亦然。 例如,多种语言使用字符æ来表示字符a以及e它们一起出现时,而字符^O可用于表示字符Ô。 使用文本比较时, Like 运算符可识别此类文化等效性。 在这种情况下,模式或字符串中的单个特殊字符匹配另一个字符串中的等效双字符序列。 同样,括在方括号(在列表中或区域中)的模式中的单个特殊字符与字符串中的等效双字符序列匹配,反之亦然。
Like在两个作数是Nothing或一个作数具有内部转换String的表达式中,Nothing另一个作数Nothing被视为空字符串文本""。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob |
| 某人 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |
| 发布者 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||
| Sh | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||
| 美国 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||
| 在 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||||
| 用户界面 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||||
| 瞧 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||||||
| UL | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||||||
| 德 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||||||||
| 四 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||||||||
| 建议做法 | 圣 | 圣 | 圣 | 圣 | Ob | |||||||||||
| 大 | 圣 | 圣 | 圣 | Ob | ||||||||||||
| Ch | 圣 | 圣 | Ob | |||||||||||||
| 圣 | 圣 | Ob | ||||||||||||||
| Ob | Ob |
串联运算符
ConcatenationOperatorExpression
: Expression '&' LineTerminator? Expression
;
为所有内部类型定义 串联运算符 ,包括固有值类型的可为 null 版本。 它还定义为上述类型与System.DBNullNothing字符串之间的串联。 串联运算符将所有作数 String转换为 ;在表达式中,无论是否使用严格的语义,所有要进行的所有转换 String 都会被视为扩大。 值System.DBNull将转换为类型化为String的文本Nothing。 一种可以为 null 的值类型,其值Nothing也转换为类型化为String文本Nothing,而不是引发运行时错误。
串联作将产生一个字符串,该字符串是两个作数的串联顺序从左到右。
Nothing该值被视为空字符串文本""。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob |
| 某人 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |
| 发布者 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||
| Sh | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||
| 美国 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||
| 在 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||||
| 用户界面 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||||
| 瞧 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||||||
| UL | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||||||
| 德 | 圣 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | |||||||||
| 四 | 圣 | 圣 | 圣 | 圣 | 圣 | Ob | ||||||||||
| 建议做法 | 圣 | 圣 | 圣 | 圣 | Ob | |||||||||||
| 大 | 圣 | 圣 | 圣 | Ob | ||||||||||||
| Ch | 圣 | 圣 | Ob | |||||||||||||
| 圣 | 圣 | Ob | ||||||||||||||
| Ob | Ob |
逻辑运算符
和AndNotOrXor运算符称为逻辑运算符。
LogicalOperatorExpression
: 'Not' Expression
| Expression 'And' LineTerminator? Expression
| Expression 'Or' LineTerminator? Expression
| Expression 'Xor' LineTerminator? Expression
;
逻辑运算符的计算方式如下:
Boolean对于类型:逻辑
And作在其两个作数上执行。逻辑
Not作在其作数上执行。逻辑
Or作在其两个作数上执行。逻辑排他
Or运算在其两个作数上执行。
对于
Byte、SByte、UShort、ShortUInteger、Integer、、ULong和Long所有枚举类型,指定作针对两个作数的二进制表示形式的每一位执行:And:如果两个位均为 1,则结果位为 1;否则,结果位为 0。Not:如果位为 0,则结果位为 1;否则,结果位为 1。Or:如果任一位为 1,则结果位为 1;否则,结果位为 0。Xor:如果任一位为 1,但不是两位,则结果位为 1;否则,结果位为 0(即 1Xor0 = 1,1 1Xor= 0)。
当逻辑运算符
And并为Or类型Boolean?提升时,它们会扩展为包含三值布尔逻辑,如下所示:And如果两个作数均为 true,则计算结果为 true;如果其中一个作数为 false,则为 false;Nothing否则。Or如果任一作数为 true,则计算结果为 true;false 是两个作数为 false;Nothing否则。
例如:
Module Test
Sub Main()
Dim x?, y? As Boolean
x = Nothing
y = True
If x Or y Then
' Will execute
End If
End Sub
End Module
注意。 理想情况下,逻辑运算符And,并将对可在布尔表达式(即实现IsTrue和IsFalse)中使用的任何类型的三值逻辑使用三值逻辑提升,其方式与在布尔表达式中使用的任何类型的短路相同AndAlsoOrElse。Or 遗憾的是,仅应用于 Boolean?三值提升,因此需要三值逻辑的用户定义类型必须手动为可为 null 的版本定义 And 和 Or 运算符。
这些操作不可能产生溢出。 枚举类型运算符对枚举类型的基础类型执行按位运算,但返回值为枚举类型。
非作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | SB | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob |
或者,Xor作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | 博 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 博 | Ob |
| 某人 | SB | Sh | Sh | 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |
| 发布者 | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||
| Sh | Sh | 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||
| 美国 | 美国 | 在 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||
| 在 | 在 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||
| 用户界面 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||||
| 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||||
| UL | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||||||
| 德 | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||||||
| 四 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | ||||||||||
| 建议做法 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 瞧 | Ob | ||||||||||||||
| Ob | Ob |
短路逻辑运算符
和AndAlsoOrElse运算符是短路版本的And运算符和Or逻辑运算符。
ShortCircuitLogicalOperatorExpression
: Expression 'AndAlso' LineTerminator? Expression
| Expression 'OrElse' LineTerminator? Expression
;
由于其短路行为,如果在计算第一个作数后已知运算符结果,则不会在运行时计算第二个作数。
短路逻辑运算符的计算方式如下:
如果作中的第一个
AndAlso作数的计算结果False为或从其IsFalse运算符返回 True,则表达式返回其第一个作数。 否则,将计算第二个作数,对两个结果执行逻辑And运算。如果作中的第一个
OrElse作数的计算结果True为或从其IsTrue运算符返回 True,则表达式返回其第一个作数。 否则,将计算第二个作数,并对其两个结果执行逻辑Or作。
为AndAlso类型Boolean定义和OrElse运算符,或者为重载以下运算符的任何类型T定义:
Public Shared Operator IsTrue(op As T) As Boolean
Public Shared Operator IsFalse(op As T) As Boolean
以及重载相应的 And 或 Or 运算符:
Public Shared Operator And(op1 As T, op2 As T) As T
Public Shared Operator Or(op1 As T, op2 As T) As T
计算 AndAlso 或 OrElse 运算符时,第一个作数只计算一次,第二个作数不计算或只计算一次。 例如,考虑以下代码:
Module Test
Function TrueValue() As Boolean
Console.Write(" True")
Return True
End Function
Function FalseValue() As Boolean
Console.Write(" False")
Return False
End Function
Sub Main()
Console.Write("And:")
If FalseValue() And TrueValue() Then
End If
Console.WriteLine()
Console.Write("Or:")
If TrueValue() Or FalseValue() Then
End If
Console.WriteLine()
Console.Write("AndAlso:")
If FalseValue() AndAlso TrueValue() Then
End If
Console.WriteLine()
Console.Write("OrElse:")
If TrueValue() OrElse FalseValue() Then
End If
Console.WriteLine()
End Sub
End Module
它输出以下结果:
And: False True
Or: True False
AndAlso: False
OrElse: True
在提升形式的 AndAlso 和 OrElse 运算符中,如果第一个作数为 null Boolean?,则计算第二个作数,但结果始终为 null Boolean?。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob |
| 某人 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | |
| 发布者 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | ||
| Sh | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | |||
| 美国 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | ||||
| 在 | 博 | 博 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | |||||
| 用户界面 | 博 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | ||||||
| 瞧 | 博 | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | |||||||
| UL | 博 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | ||||||||
| 德 | 博 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | |||||||||
| 四 | 博 | 博 | 犯 错 | 犯 错 | 博 | Ob | ||||||||||
| 建议做法 | 博 | 犯 错 | 犯 错 | 博 | Ob | |||||||||||
| 大 | 犯 错 | 犯 错 | 犯 错 | 犯 错 | ||||||||||||
| Ch | 犯 错 | 犯 错 | 犯 错 | |||||||||||||
| 圣 | 博 | Ob | ||||||||||||||
| Ob | Ob |
移位运算符
二进制运算符 << 并 >> 执行位移运算。
ShiftOperatorExpression
: Expression '<' '<' LineTerminator? Expression
| Expression '>' '>' LineTerminator? Expression
;
为类型、、UShort、UIntegerShort、和 ULongIntegerLong类型定义Byte运算符。 SByte 与其他二进制运算符不同,移位运算的结果类型将确定为运算符是仅左作数的一元运算符。 右作数的类型必须隐式转换为 Integer ,并且不能用于确定作的结果类型。
该 << 运算符使第一个作数中的位移离开由移位量指定的位数。 结果类型范围之外的高阶位将被丢弃,低序空位位置为零填充。
运算符 >> 使第一个作数中的位向右移动由移位量指定的位数。 如果左作数为正数,则丢弃低序位,如果左作数为正数,则高阶空位位置设置为零。如果左作数为正数,则设置为 1。 如果左侧作数的类型Byte为零UShortUInteger,或ULong空置的高阶位是零填充的。
移位运算符将第一个作数的基础表示形式的位移出第二个作数的数量。 如果第二个作数的值大于第一个作数中的位数,或为负数,则按原样RightOperand And SizeMaskSizeMask计算移位量:
| LeftOperand 类型 | SizeMask |
|---|---|
Byte、SByte |
7 (&H7) |
UShort、Short |
15 (&HF) |
UInteger、Integer |
31 (&H1F) |
ULong、Long |
63 (&H3F) |
如果移位量为零,则作的结果与第一个作数的值相同。 这些操作不可能产生溢出。
作类型:
| 博 | 某人 | 发布者 | Sh | 美国 | 在 | 用户界面 | 瞧 | UL | 德 | 四 | 建议做法 | 大 | Ch | 圣 | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | 由 | Sh | 美国 | 在 | UI | 瞧 | UL | 瞧 | 瞧 | 瞧 | 犯 错 | 犯 错 | 瞧 | Ob |
布尔表达式
布尔表达式是一个表达式,可以测试它是否为 true,或者是否为 false。
BooleanExpression
: Expression
;
如果按首选项顺序,可以在布尔表达式中使用类型 T :
T是Boolean或Boolean?T将转换范围扩大为BooleanT将转换范围扩大为Boolean?T定义两个伪运算符,IsTrue以及IsFalse。T具有缩小到Boolean?的转换不涉及从该Boolean转换到Boolean?的转换。T具有缩小到的转换范围Boolean。
注意。 请注意,如果 Option Strict 处于关闭状态,则会在没有编译时错误的情况下接受要接受的收缩转换 Boolean 的表达式,但如果存在,该语言仍将首选 IsTrue 运算符。 这是因为 Option Strict 只有更改语言不接受的内容,并且永远不会更改表达式的实际含义。 因此,无论怎样Option Strict,IsTrue都必须始终优先于缩小转换。
例如,以下类未定义到 Boolean的扩大转换。 因此,它在语句中使用 If 会导致调用 IsTrue 运算符。
Class MyBool
Public Shared Widening Operator CType(b As Boolean) As MyBool
...
End Operator
Public Shared Narrowing Operator CType(b As MyBool) As Boolean
...
End Operator
Public Shared Operator IsTrue(b As MyBool) As Boolean
...
End Operator
Public Shared Operator IsFalse(b As MyBool) As Boolean
...
End Operator
End Class
Module Test
Sub Main()
Dim b As New MyBool
If b Then Console.WriteLine("True")
End Sub
End Module
如果布尔表达式的类型为或转换为或转换为BooleanBoolean?,则该值为 True true;否则为 false。
否则,布尔表达式将调用IsTrue运算符,如果运算符返回,则返回TrueTrue;否则为 false(但从不调用IsFalse运算符)。
在下面的示例中, Integer 有一个收缩转换到 Boolean,因此 null Integer? 对两 Boolean? 者(生成 null Boolean)和 Boolean (引发异常)的收缩转换。 首选的收缩转换 Boolean? ,因此,将“i”的值作为布尔表达式 False。
Dim i As Integer? = Nothing
If i Then Console.WriteLine()
Lambda 表达式
lambda 表达式定义一个名为 lambda 方法的匿名方法。 Lambda 方法可以轻松地将“内联”方法传递给采用委托类型的其他方法。
LambdaExpression
: SingleLineLambda
| MultiLineLambda
;
SingleLineLambda
: LambdaModifier* 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? Expression
| 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? Statement
;
MultiLineLambda
: MultiLineFunctionLambda
| MultiLineSubLambda
;
MultiLineFunctionLambda
: LambdaModifier? 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? ( 'As' TypeName )? LineTerminator
Block
'End' 'Function'
;
MultiLineSubLambda
: LambdaModifier? 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
Block
'End' 'Sub'
;
LambdaModifier
: 'Async' | 'Iterator'
;
示例:
Module Test
Delegate Function IntFunc(x As Integer) As Integer
Sub Apply(a() As Integer, func As IntFunc)
For index As Integer = 0 To a.Length - 1
a(index) = func(a(index))
Next index
End Sub
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Apply(a, Function(x As Integer) x * 2)
For Each value In a
Console.Write(value & " ")
Next value
End Sub
End Module
将输出:
2 4 6 8
lambda 表达式以可选修饰符AsyncIterator开头,后跟关键字Function或Sub参数列表。 lambda 表达式中的参数不能声明 Optional 或 ParamArray 不能具有属性。 与常规方法不同,省略 lambda 方法的参数类型不会自动推断 Object。 相反,在重新分类 lambda 方法时,将从目标类型推断省略的参数类型和 ByRef 修饰符。 在前面的示例中,lambda 表达式可以编写为 Function(x) x * 2,并且会推断xInteger出 lambda 方法用于创建委托类型的实例IntFunc时的类型。 与局部变量推理不同,如果 lambda 方法参数省略类型但具有数组或可为 null 的名称修饰符,则会发生编译时错误。
正则 lambda 表达式既不是修饰符,也不是AsyncIterator修饰符。
迭代器 lambda 表达式是具有Iterator修饰符且无Async修饰符的表达式。 它必须是函数。 将其重新分类为某个值时,只能将其重新分类为返回类型为委托类型的值,或者IEnumerable返回类型IEnumerator或IEnumerator(Of T)IEnumerable(Of T)某些T值,并且没有 ByRef 参数。
异步 lambda 表达式是具有Async修饰符且无Iterator修饰符的表达式。 异步子 lambda 只能重新分类为没有 ByRef 参数的子委托类型的值。 异步函数 lambda 只能重新分类为函数委托类型的值,其返回类型为 Task 或 Task(Of T) 某些 T类型,并且没有 ByRef 参数。
Lambda 表达式可以是单行表达式,也可以是多行表达式。 单行 Function lambda 表达式包含表示从 lambda 方法返回的值的单个表达式。 单行 Sub lambda 表达式包含一个语句,但不包含其右对齐 StatementTerminator。 例如:
Module Test
Sub Do(a() As Integer, action As Action(Of Integer))
For index As Integer = 0 To a.Length - 1
action(a(index))
Next index
End Sub
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Do(a, Sub(x As Integer) Console.WriteLine(x))
End Sub
End Module
单行 lambda 构造与所有其他表达式和语句相比,绑定的紧密程度更低。 因此,例如,“Function() x + 5”等效于“Function() (x+5)"而不是””。(Function() x) + 5 为了避免歧义,单行 Sub lambda 表达式可能不包含 Dim 语句或标签声明语句。 此外,除非它括在括号中,否则单行 Sub lambda 表达式可能不会紧跟冒号“:”、成员访问运算符“.”、字典成员访问运算符“!”或打开的括号“(”。 它不能包含任何块语句(With、、SyncLock, If...EndIfWhile、For、Do、Using)也OnErrorResume不包含任何块语句。
注意。 在 lambda 表达式 Function(i) x=i中,正文被解释为 表达式 (用于测试是否 x 相 i 等)。 但在 lambda 表达式Sub(i) x=i中,正文被解释为语句(它赋给ix)。
多行 lambda 表达式包含语句块,必须以适当的 End 语句(即 End Function 或 End Sub) 结尾。 与常规方法一样,多行 lambda 方法 Function 或 Sub 语句 End 必须位于自己的行中。 例如:
' Error: Function statement must be on its own line!
Dim x = Sub(x As Integer) : Console.WriteLine(x) : End Sub
' OK
Dim y = Sub(x As Integer)
Console.WriteLine(x)
End Sub
多行 Function lambda 表达式可以声明返回类型,但不能将属性放在它上。 如果多行 Function lambda 表达式未声明返回类型,但可以从使用 lambda 表达式的上下文推断返回类型,则使用该返回类型。 否则,函数的返回类型按如下方式计算:
在正则 lambda 表达式中,返回类型是语句块中所有语句中的表达式的主要
Return类型。在异步 lambda 表达式中,返回类型是
Task(Of T)T语句块中所有Return语句中表达式的主要类型。在迭代器 lambda 表达式中,返回类型是
IEnumerable(Of T)T语句块中所有Yield语句中表达式的主要类型。
例如:
Function f(min As Integer, max As Integer) As IEnumerable(Of Integer)
If min > max Then Throw New ArgumentException()
Dim x = Iterator Function()
For i = min To max
Yield i
Next
End Function
' infers x to be a delegate with return type IEnumerable(Of Integer)
Return x()
End Function
在所有情况下,如果没有 Return (分别 Yield)语句,或者其中没有主导类型,并且正在使用严格的语义,则会发生编译时错误;否则,主类型是 Object隐式的。
请注意,返回类型是从所有 Return 语句计算的,即使这些语句不可访问也是如此。 例如:
' Return type is Double
Dim x = Function()
Return 10
Return 10.50
End Function
没有隐式返回变量,因为变量没有名称。
多行 lambda 表达式内的语句块具有以下限制:
On Error不允许和Resume语句,尽管Try允许语句。静态局部变量不能在多行 lambda 表达式中声明。
尽管普通分支规则在多行 lambda 表达式的语句块中应用,但无法将其分入或传出。 例如:
Label1: Dim x = Sub() ' Error: Cannot branch out GoTo Label1 ' OK: Wholly within the lamba. GoTo Label2: Label2: End Sub ' Error: Cannot branch in GoTo Label2
lambda 表达式大致相当于在包含类型上声明的匿名方法。 初始示例大致等效于:
Module Test
Delegate Function IntFunc(x As Integer) As Integer
Sub Apply(a() As Integer, func As IntFunc)
For index As Integer = 0 To a.Length - 1
a(index) = func(a(index))
Next index
End Sub
Function $Lambda1(x As Integer) As Integer
Return x * 2
End Function
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Apply(a, AddressOf $Lambda1)
For Each value In a
Console.Write(value & " ")
Next value
End Sub
End Module
闭包
Lambda 表达式有权访问作用域中的所有变量,包括包含方法和 lambda 表达式中定义的局部变量或参数。 当 lambda 表达式引用局部变量或参数时,lambda 表达式将捕获被引用到关闭的变量。 关闭是驻留在堆而不是堆栈上的对象,捕获变量时,对变量的所有引用都会重定向到关闭。 这样,即使包含方法完成,lambda 表达式也能继续引用局部变量和参数。 例如:
Module Test
Delegate Function D() As Integer
Function M() As D
Dim x As Integer = 10
Return Function() x
End Function
Sub Main()
Dim y As D = M()
' Prints 10
Console.WriteLine(y())
End Sub
End Module
大致等效于:
Module Test
Delegate Function D() As Integer
Class $Closure1
Public x As Integer
Function $Lambda1() As Integer
Return x
End Function
End Class
Function M() As D
Dim c As New $Closure1()
c.x = 10
Return AddressOf c.$Lambda1
End Function
Sub Main()
Dim y As D = M()
' Prints 10
Console.WriteLine(y())
End Sub
End Module
关闭会在每次输入声明局部变量的块时捕获局部变量的新副本,但新副本使用上一个副本的值(如果有)进行初始化。 例如:
Module Test
Delegate Function D() As Integer
Function M() As D()
Dim a(9) As D
For i As Integer = 0 To 9
Dim x
a(i) = Function() x
x += 1
Next i
Return a
End Function
Sub Main()
Dim y() As D = M()
For i As Integer = 0 To 9
Console.Write(y(i)() & " ")
Next i
End Sub
End Module
指纹
1 2 3 4 5 6 7 8 9 10
而不是
9 9 9 9 9 9 9 9 9 9
由于在进入块时必须初始化关闭,因此不允许 GoTo 关闭该块外部关闭的块,尽管允许 Resume 关闭该块进入块。 例如:
Module Test
Sub Main()
Dim a = 10
If a = 10 Then
L1:
Dim x = Function() a
' Valid, source is within block
GoTo L2
L2:
End If
' ERROR: target is inside block with closure
GoTo L1
End Sub
End Module
由于无法将其捕获到关闭中,因此在 lambda 表达式中不能显示以下内容:
引用参数。
实例表达式 (
Me, ,MyBaseMyClass如果类型Me不是类)。
如果 lambda 表达式是表达式的一部分,则为匿名类型创建表达式的成员。 例如:
' Error: Lambda cannot refer to anonymous type field
Dim x = New With { .a = 12, .b = Function() .a }
ReadOnly 实例构造函数中的实例变量或 ReadOnly 共享构造函数中的共享变量,其中变量用于非值上下文。 例如:
Class C1
ReadOnly F1 As Integer
Sub New()
' Valid, doesn't modify F1
Dim x = Function() F1
' Error, tries to modify F1
Dim f = Function() ModifyValue(F1)
End Sub
Sub ModifyValue(ByRef x As Integer)
End Sub
End Class
查询表达式
查询表达式是一个表达式,用于将一系列查询运算符应用于可查询集合的元素。 例如,以下表达式采用对象集合 Customer ,并返回华盛顿州的所有客户的名称:
Dim names = _
From cust In Customers _
Where cust.State = "WA" _
Select cust.Name
查询表达式必须以某个 From 或运算符开头, Aggregate 并且可以以任何查询运算符结尾。 查询表达式的结果被分类为值;表达式的结果类型取决于表达式中最后一个查询运算符的结果类型。
QueryExpression
: FromOrAggregateQueryOperator QueryOperator*
;
FromOrAggregateQueryOperator
: FromQueryOperator
| AggregateQueryOperator
;
QueryOperator
: FromQueryOperator
| AggregateQueryOperator
| SelectQueryOperator
| DistinctQueryOperator
| WhereQueryOperator
| OrderByQueryOperator
| PartitionQueryOperator
| LetQueryOperator
| GroupByQueryOperator
| JoinOrGroupJoinQueryOperator
;
JoinOrGroupJoinQueryOperator
: JoinQueryOperator
| GroupJoinQueryOperator
;
范围变量
某些查询运算符引入了一种称为 范围变量的特殊变量。 范围变量不是实际变量;相反,它们表示在对输入集合的查询求值期间的各个值。
CollectionRangeVariableDeclarationList
: CollectionRangeVariableDeclaration ( Comma CollectionRangeVariableDeclaration )*
;
CollectionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? 'In' LineTerminator? Expression
;
ExpressionRangeVariableDeclarationList
: ExpressionRangeVariableDeclaration ( Comma ExpressionRangeVariableDeclaration )*
;
ExpressionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? Equals Expression
;
范围变量的范围从查询运算符引入查询运算符到查询表达式的末尾或查询运算符(例如 Select 隐藏它们)的范围。 例如,在以下查询中
Dim waCusts = _
From cust As Customer In Customers _
Where cust.State = "WA"
查询From运算符引入了一个类型化的范围变量,该Customer变量cust表示集合中的每个Customers客户。 然后,以下 Where 查询运算符引用筛选器表达式中的范围变量 cust ,以确定是否将单个客户从生成的集合中筛选出来。
有两种类型的范围变量: 集合范围变量 和 表达式范围变量。 集合范围变量从要查询的集合的元素中获取其值。 集合范围变量声明中的集合表达式必须分类为可查询类型的值。 如果省略集合范围变量的类型,则将其推断为集合表达式的元素类型,或者 Object 集合表达式没有元素类型(即仅定义方法 Cast )。 如果集合表达式不可查询(即无法推断集合的元素类型),则编译时错误结果。
表达式范围变量是一个范围变量,其值由表达式而不是集合计算。 在以下示例中, Select 查询运算符引入了一个名为 cityState 从两个字段计算的表达式范围变量:
Dim cityStates = _
From cust As Customer In Customers _
Select cityState = cust.City & "," & cust.State _
Where cityState.Length() < 10
引用另一个范围变量不需要表达式范围变量,尽管此类变量可能具有可疑值。 分配给表达式范围变量的表达式必须分类为值,并且必须隐式转换为范围变量的类型(如果给定)。
只有在 Let 运算符中,表达式范围变量才能指定其类型。 在其他运算符中,或者如果未指定其类型,则使用局部变量类型推理来确定范围变量的类型。
范围变量必须遵循有关阴影声明局部变量的规则。 因此,范围变量不能隐藏封闭方法或其他范围变量中局部变量或参数的名称(除非查询运算符专门隐藏作用域中的所有当前范围变量)。
可查询类型
通过将表达式转换为对集合类型的已知方法的调用来实现查询表达式。 这些定义完善的方法定义可查询集合的元素类型,以及对集合执行的查询运算符的结果类型。 每个查询运算符指定查询运算符通常转换为的方法或方法,尽管特定的翻译依赖于实现。 这些方法以规范形式提供,其格式如下所示:
Function Select(selector As Func(Of T, R)) As CR
以下内容适用于方法:
该方法必须是集合类型的实例或扩展成员,并且必须可访问。
该方法可以是泛型方法,前提是可以推断所有类型参数。
此方法可能会重载,在这种情况下,重载解析用于确定要使用的确切方法。
如果委托类型与匹配
Func类型具有相同的签名(包括返回类型),可以使用另一种委托类型代替委托Func类型。该类型
System.Linq.Expressions.Expression(Of D)可以代替委托类型,前提是D该类型与匹配Func类型具有相同签名(包括返回类型)的委托Func类型。该类型
T表示输入集合的元素类型。 由集合类型定义的所有方法必须具有相同的输入元素类型,才能查询集合类型。此类型
S表示执行联接的查询运算符时第二个输入集合的元素类型。如果查询运算符具有一组充当键的范围变量,则此类型
K表示键类型。该类型
N表示用作数值类型的类型(尽管它可能仍然是用户定义的类型,而不是内部数值类型)。该类型
B表示可在布尔表达式中使用的类型。如果查询运算符生成结果集合,则此类型
R表示结果集合的元素类型。R取决于查询运算符结束时范围内的范围变量数。 如果单个范围变量位于范围内,则R为该范围变量的类型。 在示例中Dim custNames = From c In Customers Select c.Name查询的结果将是具有元素类型的集合类型
String。 如果多个范围变量位于范围内,则R为匿名类型,其中包含范围中的所有范围变量作为Key字段。 在示例中:Dim custNames = From c In Customers, o In c.Orders Select Name = c.Name, ProductName = o.ProductName查询的结果将是一个集合类型,其元素类型为匿名类型的元素类型,该元素类型具有一个名为
Name类型的String只读属性和一个名为ProductName类型的String只读属性。在查询表达式中,生成的匿名类型包含范围变量是 透明的,这意味着范围变量始终可用且没有限定。 例如,在前面的示例中,范围变量
co可以在查询运算符中Select访问,即使输入集合的元素类型是匿名类型也是如此。该类型
CX表示集合类型,不一定是输入集合类型,其元素类型是某种类型X。
可查询集合类型必须满足以下条件之一,按首选项顺序排列:
它必须定义一个符合性
Select的方法。它必须具有以下方法之一
Function AsEnumerable() As CT Function AsQueryable() As CT可以调用该集合来获取可查询集合。 如果提供了这两种方法,
AsQueryable则优先于AsEnumerable.它必须具有方法
Function Cast(Of T)() As CT可以使用范围变量的类型调用,以生成可查询集合。
由于确定集合的元素类型独立于实际方法调用而发生,因此无法确定特定方法的适用性。 因此,如果存在与已知方法匹配的实例方法,则确定集合的元素类型时,将忽略与已知方法匹配的任何扩展方法。
查询运算符转换按查询运算符在表达式中出现的顺序发生。 集合对象不需要实现所有查询运算符所需的所有方法,尽管每个集合对象至少必须支持 Select 查询运算符。 如果不存在所需的方法,则会发生编译时错误。 绑定已知方法名称时,出于接口和扩展方法绑定中的多个继承的目的,忽略非方法,尽管阴影语义仍然适用。 例如:
Class Q1
Public Function [Select](selector As Func(Of Integer, Integer)) As Q1
End Function
End Class
Class Q2
Inherits Q1
Public [Select] As Integer
End Class
Module Test
Sub Main()
Dim qs As New Q2()
' Error: Q2.Select still hides Q1.Select
Dim zs = From q In qs Select q
End Sub
End Module
默认查询索引器
每个可查询集合类型( T 其元素类型为且尚未具有默认属性)都被视为具有以下常规形式的默认属性:
Public ReadOnly Default Property Item(index As Integer) As T
Get
Return Me.ElementAtOrDefault(index)
End Get
End Property
只能使用默认属性访问语法引用默认属性;默认属性不能按名称引用。 例如:
Dim customers As IEnumerable(Of Customer) = ...
Dim customerThree = customers(2)
' Error, no such property
Dim customerFour = customers.Item(4)
如果集合类型没有成员 ElementAtOrDefault ,将发生编译时错误。
从查询运算符
查询 From 运算符引入了一个集合范围变量,该变量表示要查询的集合的各个成员。
FromQueryOperator
: LineTerminator? 'From' LineTerminator? CollectionRangeVariableDeclarationList
;
例如,查询表达式:
From c As Customer In Customers ...
可视为等效项
For Each c As Customer In Customers
...
Next c
From当查询运算符声明多个集合范围变量或不是查询表达式中的第一个From查询运算符时,每个新的集合范围变量将交叉联接到现有的范围变量集。 结果是,查询是通过联接集合中所有元素的交叉乘积计算的。 例如,表达式:
From c In Customers _
From e In Employees _
...
可以视为等效于:
For Each c In Customers
For Each e In Employees
...
Next e
Next c
与:
From c In Customers, e In Employees ...
以前的查询运算符中引入的范围变量可用于后面的 From 查询运算符。 例如,在以下查询表达式中,第二 From 个查询运算符引用第一个范围变量的值:
From c As Customer In Customers _
From o As Order In c.Orders _
Select c.Name, o
仅当集合类型包含以下一种或两种方法时,才支持查询 From 运算符或多个查询运算符中的多个 From 范围变量:
Function SelectMany(selector As Func(Of T, CR)) As CR
Function SelectMany(selector As Func(Of T, CS), _
resultsSelector As Func(Of T, S, R)) As CR
代码
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs, y In ys ...
通常转换为
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.SelectMany( _
Function(x As Integer) ys, _
Function(x As Integer, y As Integer) New With {x, y})...
注意。
From 不是保留字。
联接查询运算符
查询 Join 运算符使用新的集合范围变量联接现有范围变量,生成一个集合,该集合的元素已基于相等表达式联接在一起。
JoinQueryOperator
: LineTerminator? 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
;
JoinConditionList
: JoinCondition ( 'And' LineTerminator? JoinCondition )*
;
JoinCondition
: Expression 'Equals' LineTerminator? Expression
;
例如:
Dim customersAndOrders = _
From cust In Customers _
Join ord In Orders On cust.ID Equals ord.CustomerID
相等表达式比正则相等表达式更受限:
这两个表达式都必须分类为值。
这两个表达式必须引用至少一个范围变量。
联接查询运算符中声明的范围变量必须由其中一个表达式引用,并且该表达式不得引用任何其他范围变量。
如果两个表达式的类型不完全相同,则
如果为这两种类型定义了相等运算符,则两个表达式均可隐式转换为它,并且不能
Object,然后将这两个表达式转换为该类型。否则,如果两个表达式都可以隐式转换为主类型,则将这两个表达式转换为该类型。
否则,将发生编译时错误。
表达式使用哈希值(即通过调用 GetHashCode())进行比较,而不是使用相等运算符提高效率。 查询运算符可以在同一 Join 运算符中执行多个联接或相等条件。
Join仅当集合类型包含方法时,才支持查询运算符:
Function Join(inner As CS, _
outerSelector As Func(Of T, K), _
innerSelector As Func(Of S, K), _
resultSelector As Func(Of T, S, R)) As CR
代码
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
Join y In ys On x Equals y _
...
通常转换为
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.Join( _
ys, _
Function(x As Integer) x, _
Function(y As Integer) y, _
Function(x As Integer, y As Integer) New With {x, y})...
注意。JoinOn并且Equals不是保留字。
Let Query 运算符
查询 Let 运算符引入了表达式范围变量。 这样就可以在以后的查询运算符中多次使用一次中间值。
LetQueryOperator
: LineTerminator? 'Let' LineTerminator? ExpressionRangeVariableDeclarationList
;
例如:
Dim taxedPrices = _
From o In Orders _
Let tax = o.Price * 0.088 _
Where tax > 3.50 _
Select o.Price, tax, total = o.Price + tax
可以视为等效于:
For Each o In Orders
Dim tax = o.Price * 0.088
...
Next o
Let仅当集合类型包含方法时,才支持查询运算符:
Function Select(selector As Func(Of T, R)) As CR
代码
Dim xs() As Integer = ...
Dim zs = From x In xs _
Let y = x * 10 _
...
通常转换为
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
选择查询运算符
查询 Select 运算符类似于 Let 查询运算符,因为它引入了表达式范围变量;但是, Select 查询运算符隐藏当前可用的范围变量,而不是添加到它们。 此外,查询运算符引入 Select 的表达式范围变量的类型始终使用局部变量类型推理规则进行推断;无法指定显式类型;如果无法推断类型,则会发生编译时错误。
SelectQueryOperator
: LineTerminator? 'Select' LineTerminator? ExpressionRangeVariableDeclarationList
;
例如,在查询中:
Dim smiths = _
From cust In Customers _
Select name = cust.name _
Where name.EndsWith("Smith")
Where查询运算符仅有权访问name运算符引入Select的范围变量;如果Where运算符尝试引用cust,则会发生编译时错误。
查询运算符可以使用与匿名类型对象创建表达式相同的规则来推断范围变量的名称, Select 而不是显式指定范围变量的名称。 例如:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.name, ord.ProductName _
Where name.EndsWith("Smith")
如果未提供范围变量的名称,并且无法推断名称,则会发生编译时错误。
Select如果查询运算符仅包含单个表达式,则无法推断该范围变量的名称但范围变量是无名称的,则不会发生错误。 例如:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.Name & " bought " & ord.ProductName _
Take 10
如果在将名称分配给范围变量和相等表达式之间查询运算符存在 Select 歧义,则首选名称赋值。 例如:
Dim badCustNames = _
From c In Customers _
Let name = "John Smith" _
Select name = c.Name ' Creates a range variable named "name"
Dim goodCustNames = _
From c In Customers _
Let name = "John Smith" _
Select match = (name = c.Name)
查询运算符中的每个 Select 表达式都必须分类为一个值。
Select仅当集合类型包含方法时,才支持查询运算符:
Function Select(selector As Func(Of T, R)) As CR
代码
Dim xs() As Integer = ...
Dim zs = From x In xs _
Select x, y = x * 10 _
...
通常转换为
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
Distinct Query 运算符
查询 Distinct 运算符仅将集合中的值限制为具有非重复值的值,这通过比较元素类型的相等性来确定。
DistinctQueryOperator
: LineTerminator? 'Distinct' LineTerminator?
;
例如,查询:
Dim distinctCustomerPrice = _
From cust In Customers, ord In cust.Orders _
Select cust.Name, ord.Price _
Distinct
即使客户具有具有相同价格的多个订单,也会为每个不同的客户名称和订单价格配对返回一行。
Distinct仅当集合类型包含方法时,才支持查询运算符:
Function Distinct() As CT
代码
Dim xs() As Integer = ...
Dim zs = From x In xs _
Distinct _
...
通常转换为
Dim xs() As Integer = ...
Dim zs = xs.Distinct()...
注意。
Distinct 不是保留字。
Where Query 运算符
查询 Where 运算符将集合中的值限制为满足给定条件的值。
WhereQueryOperator
: LineTerminator? 'Where' LineTerminator? BooleanExpression
;
Where查询运算符采用针对每个范围变量值集计算的布尔表达式;如果表达式的值为 true,则值将显示在输出集合中,否则将跳过这些值。 例如,查询表达式:
From cust In Customers, ord In Orders _
Where cust.ID = ord.CustomerID _
...
可被视为等效于嵌套循环
For Each cust In Customers
For Each ord In Orders
If cust.ID = ord.CustomerID Then
...
End If
Next ord
Next cust
Where仅当集合类型包含方法时,才支持查询运算符:
Function Where(predicate As Func(Of T, B)) As CT
代码
Dim xs() As Integer = ...
Dim zs = From x In xs _
Where x < 10 _
...
通常转换为
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x As Integer) x < 10)...
注意。
Where 不是保留字。
分区查询运算符
PartitionQueryOperator
: LineTerminator? 'Take' LineTerminator? Expression
| LineTerminator? 'Take' 'While' LineTerminator? BooleanExpression
| LineTerminator? 'Skip' LineTerminator? Expression
| LineTerminator? 'Skip' 'While' LineTerminator? BooleanExpression
;
查询 Take 运算符生成集合的第一 n 个元素。 与修饰符一起使用 While 时, Take 运算符将生成满足布尔表达式的集合的第一 n 个元素。 运算符 Skip 跳过集合的第一 n 个元素,然后返回集合的其余部分。 与修饰符一起使用 While 时, Skip 运算符将跳过满足布尔表达式的集合的第一 n 个元素,然后返回集合的其余部分。 或查询运算符中的TakeSkip表达式必须分类为值。
Take仅当集合类型包含方法时,才支持查询运算符:
Function Take(count As N) As CT
Skip仅当集合类型包含方法时,才支持查询运算符:
Function Skip(count As N) As CT
Take While仅当集合类型包含方法时,才支持查询运算符:
Function TakeWhile(predicate As Func(Of T, B)) As CT
Skip While仅当集合类型包含方法时,才支持查询运算符:
Function SkipWhile(predicate As Func(Of T, B)) As CT
代码
Dim xs() As Integer = ...
Dim zs = From x In xs _
Skip 10 _
Take 5 _
Skip While x < 10 _
Take While x > 5 _
...
通常转换为
Dim xs() As Integer = ...
Dim zs = _
xs.Skip(10). _
Take(5). _
SkipWhile(Function(x) x < 10). _
TakeWhile(Function(x) x > 5)...
注意。
Take 不是 Skip 保留字。
Order by Query 运算符
查询 Order By 运算符对范围变量中显示的值进行排序。
OrderByQueryOperator
: LineTerminator? 'Order' 'By' LineTerminator? OrderExpressionList
;
OrderExpressionList
: OrderExpression ( Comma OrderExpression )*
;
OrderExpression
: Expression Ordering?
;
Ordering
: 'Ascending' | 'Descending'
;
Order By查询运算符采用指定用于对迭代变量进行排序的键值的表达式。 例如,以下查询返回按价格排序的产品:
Dim productsByPrice = _
From p In Products _
Order By p.Price _
Select p.Name
排序可以标记为 Ascending,在这种情况下,较小的值出现在较大的值之前,或者 Descending在这种情况下,较大的值出现在较小的值之前。 如果未指定 Ascending任何值,则为排序的默认值。 例如,以下查询返回按价格排序的产品,最昂贵的产品首先:
Dim productsByPriceDesc = _
From p In Products _
Order By p.Price Descending _
Select p.Name
查询 Order By 运算符可以指定多个表达式进行排序,在这种情况下,集合按嵌套方式排序。 例如,以下查询按州/自治区、市/自治区内的市/自治区和每个城市的邮政编码对客户进行排序:
Dim customersByLocation = _
From c In Customers _
Order By c.State, c.City, c.ZIP _
Select c.Name, c.State, c.City, c.ZIP
查询运算符中的 Order By 表达式必须分类为值。
Order By仅当集合类型包含以下一种或两种方法时,才支持查询运算符:
Function OrderBy(keySelector As Func(Of T, K)) As CT
Function OrderByDescending(keySelector As Func(Of T, K)) As CT
返回类型 CT 必须是 有序集合。 有序集合是包含一种或两种方法的集合类型:
Function ThenBy(keySelector As Func(Of T, K)) As CT
Function ThenByDescending(keySelector As Func(Of T, K)) As CT
代码
Dim xs() As Integer = ...
Dim zs = From x In xs _
Order By x Ascending, x Mod 2 Descending _
...
通常转换为
Dim xs() As Integer = ...
Dim zs = _
xs.OrderBy(Function(x) x).ThenByDescending(Function(x) x Mod 2)...
注意。 由于查询运算符只是将语法映射到实现特定查询作的方法,因此顺序保留不由语言决定,由运算符本身的实现决定。 这与用户定义的运算符非常相似,因此,用于重载用户定义数值类型的加法运算符的实现可能无法执行任何类似于加法的任何作。 当然,为了保持可预测性,不建议实现与用户期望不匹配的内容。
注意。
Order 不是 By 保留字。
按查询运算符分组
查询 Group By 运算符基于一个或多个表达式对范围中的范围变量进行分组,然后基于这些分组生成新的范围变量。
GroupByQueryOperator
: LineTerminator? 'Group' ( LineTerminator? ExpressionRangeVariableDeclarationList )?
LineTerminator? 'By' LineTerminator? ExpressionRangeVariableDeclarationList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
例如,以下查询按 State以下查询对所有客户进行分组,然后计算每个组的计数和平均年龄:
Dim averageAges = _
From cust In Customers _
Group By cust.State _
Into Count(), Average(cust.Age)
查询 Group By 运算符有三个子句:可选 Group 子句、 By 子句和 Into 子句。 子 Group 句的语法和效果与 Select 查询运算符相同,只影响子句中 Into 可用的范围变量,而不是 By 子句。 例如:
Dim averageAges = _
From cust In Customers _
Group cust.Age By cust.State _
Into Count(), Average(Age)
子 By 句声明在分组作中用作键值的表达式范围变量。 该 Into 子句允许声明表达式范围变量,这些变量计算由 By 子句构成的每个组的聚合。 在该子句中 Into ,表达式范围变量只能分配一个表达式,该表达式是 聚合函数的方法调用。 聚合函数是组集合类型的函数(可能不一定与原始集合的集合类型相同),类似于以下方法之一:
Function _name_() As _type_
Function _name_(selector As Func(Of T, R)) As R
如果聚合函数采用委托参数,则调用表达式可以具有必须分类为值的参数表达式。 参数表达式可以使用范围中的范围变量;在对聚合函数的调用中,这些范围变量表示正在构成的组中的值,而不是集合中的所有值。 例如,在本部分中 Average 的原始示例中,该函数计算每个状态的客户年龄的平均值,而不是针对所有客户。
所有集合类型都被视为对其定义了聚合函数,该函数 Group 不采用任何参数,并且只返回组。 集合类型可能提供的其他标准聚合函数包括:
Count , LongCount返回组中元素的计数或满足布尔表达式的组中元素的计数。
Count 并且 LongCount 仅当集合类型包含其中一种方法时才受支持:
Function Count() As N
Function Count(selector As Func(Of T, B)) As N
Function LongCount() As N
Function LongCount(selector As Func(Of T, B)) As N
Sum,它返回组中所有元素的表达式的总和。
Sum 仅当集合类型包含其中一种方法时,才支持:
Function Sum() As N
Function Sum(selector As Func(Of T, N)) As N
Min 这将返回组中所有元素的表达式的最小值。
Min 仅当集合类型包含其中一种方法时,才支持:
Function Min() As N
Function Min(selector As Func(Of T, N)) As N
Max,它返回组中所有元素的表达式的最大值。
Max 仅当集合类型包含其中一种方法时,才支持:
Function Max() As N
Function Max(selector As Func(Of T, N)) As N
Average,返回组中所有元素的表达式的平均值。
Average 仅当集合类型包含其中一种方法时,才支持:
Function Average() As N
Function Average(selector As Func(Of T, N)) As N
Any,它确定组是否包含成员,或者组中任何元素的布尔表达式是否为 true。
Any 返回一个值,该值可用于布尔表达式,并且仅当集合类型包含其中一种方法时才受支持:
Function Any() As B
Function Any(predicate As Func(Of T, B)) As B
All,用于确定组中所有元素的布尔表达式是否为 true。
All 返回一个值,该值可用于布尔表达式,并且仅当集合类型包含方法时才受支持:
Function All(predicate As Func(Of T, B)) As B
Group By查询运算符后,以前在作用域中的范围变量将隐藏,并且可以使用由和Into子句引入By的范围变量。
Group By仅当集合类型包含该方法时,才支持查询运算符:
Function GroupBy(keySelector As Func(Of T, K), _
resultSelector As Func(Of K, CT, R)) As CR
仅当集合类型包含该方法时,才支持子句中的 Group 范围变量声明:
Function GroupBy(keySelector As Func(Of T, K), _
elementSelector As Func(Of T, S), _
resultSelector As Func(Of K, CS, R)) As CR
代码
Dim xs() As Integer = ...
Dim zs = From x In xs _
Group y = x * 10, z = x / 10 By evenOdd = x Mod 2 _
Into Sum(y), Average(z) _
...
通常转换为
Dim xs() As Integer = ...
Dim zs = _
xs.GroupBy( _
Function(x As Integer) x Mod 2, _
Function(x As Integer) New With {.y = x * 10, .z = x / 10}, _
Function(evenOdd, group) New With { _
evenOdd, _
.Sum = group.Sum(Function(e) e.y), _
.Average = group.Average(Function(e) e.z)})...
注意.Group, By并且 Into 不是保留字。
聚合查询运算符
查询 Aggregate 运算符执行与 Group By 运算符类似的函数,只不过它允许聚合已形成的组。 由于该组已形成, Into 因此查询运算符的 Aggregate 子句不会隐藏作用域中的范围变量(这样 Aggregate ,就更像是一个 Let,并且 Group By 更像是一个 Select)。
AggregateQueryOperator
: LineTerminator? 'Aggregate' LineTerminator? CollectionRangeVariableDeclaration QueryOperator*
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
例如,以下查询聚合了客户在华盛顿下达的所有订单总数:
Dim orderTotals = _
From cust In Customers _
Where cust.State = "WA" _
Aggregate order In cust.Orders _
Into Sum(order.Total)
此查询的结果是一个集合,其元素类型是一个匿名类型,其属性名为 cust typed as Customer 和一个名为 Sum typed 的属性。Integer
与不同Group By,可以在子Into句之间Aggregate放置其他查询运算符。 在 Aggregate 子句和子句末尾 Into 之间,可以使用作用域中的所有范围变量,包括子句声明的 Aggregate 变量。 例如,以下查询聚合了 2006 年之前在华盛顿客户下达的所有订单的总和:
Dim orderTotals = _
From cust In Customers _
Where cust.State = "WA" _
Aggregate order In cust.Orders _
Where order.Date <= #01/01/2006# _
Into Sum = Sum(order.Total)
该 Aggregate 运算符还可用于启动查询表达式。 在这种情况下,查询表达式的结果将是子句计算的 Into 单个值。 例如,以下查询计算 2006 年 1 月 1 日之前的所有订单总计的总和:
Dim ordersTotal = _
Aggregate order In Orders _
Where order.Date <= #01/01/2006# _
Into Sum(order.Total)
查询的结果是单个 Integer 值。
Aggregate查询运算符始终可用(尽管聚合函数也必须可用于表达式有效)。 代码
Dim xs() As Integer = ...
Dim zs = _
Aggregate x In xs _
Where x < 5 _
Into Sum()
通常转换为
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x) x < 5).Sum()
注意。
Aggregate 不是 Into 保留字。
组联接查询运算符
查询Group Join运算符将查询运算符和查询运算符的JoinGroup By函数合并为单个运算符。
Group Join 基于从元素中提取的匹配键联接两个集合,将联接右侧的所有元素组合在一起,这些元素与联接左侧的特定元素匹配。 因此,运算符生成一组分层结果。
GroupJoinQueryOperator
: LineTerminator? 'Group' 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
例如,以下查询生成包含单个客户名称的元素、一组所有订单以及所有这些订单的总金额:
Dim custsWithOrders = _
From cust In Customers _
Group Join order In Orders On cust.ID Equals order.CustomerID _
Into Orders = Group, OrdersTotal = Sum(order.Total) _
Select cust.Name, Orders, OrdersTotal
查询的结果是一个集合,其元素类型是一个匿名类型,其三个属性: Name类型为 String, Orders 类型化,类型为其元素类型为 Order的集合,并 OrdersTotal键入为 Integer。
Group Join仅当集合类型包含该方法时,才支持查询运算符:
Function GroupJoin(inner As CS, _
outerSelector As Func(Of T, K), _
innerSelector As Func(Of S, K), _
resultSelector As Func(Of T, CS, R)) As CR
代码
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
Group Join y in ys On x Equals y _
Into g = Group _
...
通常转换为
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.GroupJoin( _
ys, _
Function(x As Integer) x, _
Function(y As Integer) y, _
Function(x, group) New With {x, .g = group})...
注意.Group, Join并且 Into 不是保留字。
条件表达式
条件 If 表达式测试表达式并返回值。
ConditionalExpression
: 'If' OpenParenthesis BooleanExpression Comma Expression Comma Expression CloseParenthesis
| 'If' OpenParenthesis Expression Comma Expression CloseParenthesis
;
IIF但是,与运行时函数不同,条件表达式仅在必要时计算其作数。 因此,例如,如果值为 cNothing,则表达式If(c Is Nothing, c.Name, "Unknown")不会引发异常。 条件表达式有两种形式:一种采用两个作数,一个采用三个作数。
如果提供了三个作数,则所有三个表达式都必须分类为值,第一个作数必须是布尔表达式。 如果结果为表达式 true,则第二个表达式将是运算符的结果,否则第三个表达式将是该运算符的结果。 表达式的结果类型是第二和第三个表达式的类型之间的主导类型。 如果没有主类型,则会发生编译时错误。
如果提供了两个作数,则两个作数都必须分类为值,并且第一个作数必须是引用类型或可为 null 的值类型。
If(x, y)然后,表达式的计算结果与表达式If(x IsNot Nothing, x, y)一样,有两个例外。 首先,第一个表达式只计算一次,第二个作数的类型是不可为 null 的值类型,并且第一个作数的类型是,则在确定表达式结果类型的主要类型时, ? 将从第一个作数的类型中删除。 例如:
Module Test
Sub Main()
Dim x?, y As Integer
Dim a?, b As Long
a = If(x, a) ' Result type: Long?
y = If(x, 0) ' Result type: Integer
End Sub
End Module
在表达式的两种形式中,如果作数为 Nothing,则其类型不用于确定主导类型。 在表达式 If(<expression>, Nothing, Nothing)中,主要类型被视为 Object。
XML 文本表达式
XML 文本表达式表示 XML (eXtensible 标记语言) 1.0 值。
XMLLiteralExpression
: XMLDocument
| XMLElement
| XMLProcessingInstruction
| XMLComment
| XMLCDATASection
;
XML 文本表达式的结果是类型化为命名空间中的 System.Xml.Linq 类型之一的值。 如果该命名空间中的类型不可用,则 XML 文本表达式将导致编译时错误。 这些值是通过从 XML 文本表达式转换的构造函数调用生成的。 例如,代码:
Dim book As System.Xml.Linq.XElement = _
<book title="My book"></book>
大致等效于代码:
Dim book As System.Xml.Linq.XElement = _
New System.Xml.Linq.XElement( _
"book", _
New System.Xml.Linq.XAttribute("title", "My book"))
XML 文本表达式可以采用 XML 文档、XML 元素、XML 处理指令、XML 注释或 CDATA 节的形式。
注意。 此规范仅包含足够的 XML 说明来描述 Visual Basic 语言的行为。 有关 XML 的详细信息,请参阅 http://www.w3.org/TR/REC-xml/。
词法规则
XMLCharacter
: '<Unicode tab character (0x0009)>'
| '<Unicode linefeed character (0x000A)>'
| '<Unicode carriage return character (0x000D)>'
| '<Unicode characters 0x0020 - 0xD7FF>'
| '<Unicode characters 0xE000 - 0xFFFD>'
| '<Unicode characters 0x10000 - 0x10FFFF>'
;
XMLString
: XMLCharacter+
;
XMLWhitespace
: XMLWhitespaceCharacter+
;
XMLWhitespaceCharacter
: '<Unicode carriage return character (0x000D)>'
| '<Unicode linefeed character (0x000A)>'
| '<Unicode space character (0x0020)>'
| '<Unicode tab character (0x0009)>'
;
XMLNameCharacter
: XMLLetter
| XMLDigit
| '.'
| '-'
| '_'
| ':'
| XMLCombiningCharacter
| XMLExtender
;
XMLNameStartCharacter
: XMLLetter
| '_'
| ':'
;
XMLName
: XMLNameStartCharacter XMLNameCharacter*
;
XMLLetter
: '<Unicode character as defined in the Letter production of the XML 1.0 specification>'
;
XMLDigit
: '<Unicode character as defined in the Digit production of the XML 1.0 specification>'
;
XMLCombiningCharacter
: '<Unicode character as defined in the CombiningChar production of the XML 1.0 specification>'
;
XMLExtender
: '<Unicode character as defined in the Extender production of the XML 1.0 specification>'
;
XML 文本表达式使用 XML 的词法规则而不是常规 Visual Basic 代码的词法规则进行解释。 这两组规则在以下方面通常有所不同:
空白在 XML 中非常重要。 因此,XML 文本表达式的语法显式表示允许空格的位置。 不保留空格,除非它在元素中的字符数据的上下文中发生。 例如:
' The following element preserves no whitespace Dim e1 = _ <customer> <name>Bob</> </> ' The following element preserves all of the whitespace Dim e2 = _ <customer> Bob </>XML 行尾空格根据 XML 规范规范化。
XML 区分大小写。 关键字必须与大小写完全匹配,否则会发生编译时错误。
行终止符在 XML 中被视为空白。 因此,XML 文本表达式中不需要行继续字符。
XML 不接受全角字符。 如果使用全角字符,将发生编译时错误。
嵌入的表达式
XML 文本表达式可以包含 嵌入的表达式。 嵌入表达式是一个 Visual Basic 表达式,用于在嵌入表达式的位置填充一个或多个值。
XMLEmbeddedExpression
: '<' '%' '=' LineTerminator? Expression LineTerminator? '%' '>'
;
例如,以下代码将字符串 John Smith 放置为 XML 元素的值:
Dim name as String = "John Smith"
Dim element As System.Xml.Linq.XElement = <customer><%= name %></customer>
表达式可以嵌入多个上下文中。 例如,以下代码生成一个名为 customer:
Dim name As String = "customer"
Dim element As System.Xml.Linq.XElement = <<%= name %>>John Smith</>
可以使用嵌入表达式的每个上下文指定将接受的类型。 当在嵌入表达式的表达式部分的上下文中时,Visual Basic 代码的正常词法规则仍适用,例如,必须使用行延续:
' Visual Basic expression uses line continuation, XML does not
Dim element As System.Xml.Linq.XElement = _
<<%= name & _
name %>>John
Smith</>
XML 文档
XMLDocument
: XMLDocumentPrologue XMLMisc* XMLDocumentBody XMLMisc*
;
XMLDocumentPrologue
: '<' '?' 'xml' XMLVersion XMLEncoding? XMLStandalone? XMLWhitespace? '?' '>'
;
XMLVersion
: XMLWhitespace 'version' XMLWhitespace? '=' XMLWhitespace? XMLVersionNumberValue
;
XMLVersionNumberValue
: SingleQuoteCharacter '1' '.' '0' SingleQuoteCharacter
| DoubleQuoteCharacter '1' '.' '0' DoubleQuoteCharacter
;
XMLEncoding
: XMLWhitespace 'encoding' XMLWhitespace? '=' XMLWhitespace? XMLEncodingNameValue
;
XMLEncodingNameValue
: SingleQuoteCharacter XMLEncodingName SingleQuoteCharacter
| DoubleQuoteCharacter XMLEncodingName DoubleQuoteCharacter
;
XMLEncodingName
: XMLLatinAlphaCharacter XMLEncodingNameCharacter*
;
XMLEncodingNameCharacter
: XMLUnderscoreCharacter
| XMLLatinAlphaCharacter
| XMLNumericCharacter
| XMLPeriodCharacter
| XMLDashCharacter
;
XMLLatinAlphaCharacter
: '<Unicode Latin alphabetic character (0x0041-0x005a, 0x0061-0x007a)>'
;
XMLNumericCharacter
: '<Unicode digit character (0x0030-0x0039)>'
;
XMLHexNumericCharacter
: XMLNumericCharacter
| '<Unicode Latin hex alphabetic character (0x0041-0x0046, 0x0061-0x0066)>'
;
XMLPeriodCharacter
: '<Unicode period character (0x002e)>'
;
XMLUnderscoreCharacter
: '<Unicode underscore character (0x005f)>'
;
XMLDashCharacter
: '<Unicode dash character (0x002d)>'
;
XMLStandalone
: XMLWhitespace 'standalone' XMLWhitespace? '=' XMLWhitespace? XMLYesNoValue
;
XMLYesNoValue
: SingleQuoteCharacter XMLYesNo SingleQuoteCharacter
| DoubleQuoteCharacter XMLYesNo DoubleQuoteCharacter
;
XMLYesNo
: 'yes'
| 'no'
;
XMLMisc
: XMLComment
| XMLProcessingInstruction
| XMLWhitespace
;
XMLDocumentBody
: XMLElement
| XMLEmbeddedExpression
;
XML 文档导致类型化为 System.Xml.Linq.XDocument. 与 XML 1.0 规范不同,需要 XML 文本表达式中的 XML 文档来指定 XML 文档序幕;不带 XML 文档序幕的 XML 文本表达式被解释为其单个实体。 例如:
Dim doc As System.Xml.Linq.XDocument = _
<?xml version="1.0"?>
<?instruction?>
<customer>Bob</>
Dim pi As System.Xml.Linq.XProcessingInstruction = _
<?instruction?>
XML 文档可以包含一个嵌入表达式,其类型可以是任意类型;但是,在运行时,对象必须满足构造函数的要求 XDocument ,否则会发生运行时错误。
与常规 XML 不同,XML 文档表达式不支持 DTD(文档类型声明)。 此外,将忽略编码属性(如果提供),因为 Xml 文本表达式的编码始终与源文件本身的编码相同。
注意。 尽管忽略了编码属性,但它仍然是有效的属性,以便保持在源代码中包含任何有效的 Xml 1.0 文档的能力。
XML 元素
XMLElement
: XMLEmptyElement
| XMLElementStart XMLContent XMLElementEnd
;
XMLEmptyElement
: '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '/' '>'
;
XMLElementStart
: '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '>'
;
XMLElementEnd
: '<' '/' '>'
| '<' '/' XMLQualifiedName XMLWhitespace? '>'
;
XMLContent
: XMLCharacterData? ( XMLNestedContent XMLCharacterData? )+
;
XMLCharacterData
: '<Any XMLCharacterDataString that does not contain the string "]]>">'
;
XMLCharacterDataString
: '<Any Unicode character except < or &>'+
;
XMLNestedContent
: XMLElement
| XMLReference
| XMLCDATASection
| XMLProcessingInstruction
| XMLComment
| XMLEmbeddedExpression
;
XMLAttribute
: XMLWhitespace XMLAttributeName XMLWhitespace? '=' XMLWhitespace? XMLAttributeValue
| XMLWhitespace XMLEmbeddedExpression
;
XMLAttributeName
: XMLQualifiedNameOrExpression
| XMLNamespaceAttributeName
;
XMLAttributeValue
: DoubleQuoteCharacter XMLAttributeDoubleQuoteValueCharacter* DoubleQuoteCharacter
| SingleQuoteCharacter XMLAttributeSingleQuoteValueCharacter* SingleQuoteCharacter
| XMLEmbeddedExpression
;
XMLAttributeDoubleQuoteValueCharacter
: '<Any XMLCharacter except <, &, or DoubleQuoteCharacter>'
| XMLReference
;
XMLAttributeSingleQuoteValueCharacter
: '<Any XMLCharacter except <, &, or SingleQuoteCharacter>'
| XMLReference
;
XMLReference
: XMLEntityReference
| XMLCharacterReference
;
XMLEntityReference
: '&' XMLEntityName ';'
;
XMLEntityName
: 'lt' | 'gt' | 'amp' | 'apos' | 'quot'
;
XMLCharacterReference
: '&' '#' XMLNumericCharacter+ ';'
| '&' '#' 'x' XMLHexNumericCharacter+ ';'
;
XML 元素导致类型化为 System.Xml.Linq.XElement. 与常规 XML 不同,XML 元素可以省略结束标记中的名称,并且当前最嵌套的元素将被关闭。 例如:
Dim name = <name>Bob</>
XML 元素中的属性声明会导致类型化为 System.Xml.Linq.XAttribute. 根据 XML 规范规范化属性值。 如果未创建特性 Nothing 的值,则属性值表达式不必检查 Nothing。 例如:
Dim expr = Nothing
' Throws null argument exception
Dim direct = New System.Xml.Linq.XElement( _
"Name", _
New System.Xml.Linq.XAttribute("Length", expr))
' Doesn't throw exception, the result is <Name/>
Dim literal = <Name Length=<%= expr %>/>
XML 元素和属性可以包含以下位置的嵌套表达式:
元素的名称,在这种情况下,嵌入表达式必须是可隐式转换为 System.Xml.Linq.XName的类型的值。 例如:
Dim name = <<%= "name" %>>Bob</>
元素的属性的名称,在这种情况下,嵌入表达式必须是可隐式转换为 System.Xml.Linq.XName的类型的值。 例如:
Dim name = <name <%= "length" %>="3">Bob</>
元素的属性的值,在这种情况下,嵌入表达式可以是任何类型的值。 例如:
Dim name = <name length=<%= 3 %>>Bob</>
元素的属性,在这种情况下,嵌入表达式可以是任何类型的值。 例如:
Dim name = <name <%= new XAttribute("length", 3) %>>Bob</>
元素的内容,在这种情况下,嵌入表达式可以是任何类型的值。 例如:
Dim name = <name><%= "Bob" %></>
如果嵌入表达式的类型为 Object(),则数组将作为参数 XElement 传递给构造函数。
XML 命名空间
XML 元素可以包含 XML 命名空间声明,如 XML 命名空间 1.0 规范所定义。
XMLNamespaceAttributeName
: XMLPrefixedNamespaceAttributeName
| XMLDefaultNamespaceAttributeName
;
XMLPrefixedNamespaceAttributeName
: 'xmlns' ':' XMLNamespaceName
;
XMLDefaultNamespaceAttributeName
: 'xmlns'
;
XMLNamespaceName
: XMLNamespaceNameStartCharacter XMLNamespaceNameCharacter*
;
XMLNamespaceNameStartCharacter
: '<Any XMLNameCharacter except :>'
;
XMLNamespaceNameCharacter
: XMLLetter
| '_'
;
XMLQualifiedNameOrExpression
: XMLQualifiedName
| XMLEmbeddedExpression
;
XMLQualifiedName
: XMLPrefixedName
| XMLUnprefixedName
;
XMLPrefixedName
: XMLNamespaceName ':' XMLNamespaceName
;
XMLUnprefixedName
: XMLNamespaceName
;
定义命名空间 xml 并强制执行的限制, xmlns 并生成编译时错误。 XML 命名空间声明不能为其值具有嵌入表达式;提供的值必须是非空字符串文本。 例如:
' Declares a valid namespace
Dim customer = <db:customer xmlns:db="http://example.org/database">Bob</>
' Error: xmlns cannot be re-defined
Dim bad1 = <elem xmlns:xmlns="http://example.org/namespace"/>
' Error: cannot have an embedded expression
Dim bad2 = <elem xmlns:db=<%= "http://example.org/database" %>>Bob</>
注意。 此规范仅包含足够的 XML 命名空间说明来描述 Visual Basic 语言的行为。 有关 XML 命名空间的详细信息,请参阅
可以使用命名空间名称限定 XML 元素和属性名称。 命名空间与常规 XML 中一样绑定,例外情况是,在文件级别声明的任何命名空间导入都被视为在包含声明的上下文中声明,该声明本身由编译环境声明的任何命名空间导入所括起来。 如果找不到命名空间名称,则会发生编译时错误。 例如:
Imports System.Xml.Linq
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
' Binds to the imported namespace above.
Dim c1 = <db:customer>Bob</>
' Binds to the namespace declaration in the element
Dim c2 = _
<db:customer xmlns:db="http://example.org/database-other">Mary</>
' Binds to the inner namespace declaration
Dim c3 = _
<database xmlns:db="http://example.org/database-one">
<db:customer xmlns:db="http://example.org/database-two">Joe</>
</>
' Error: namespace db2 cannot be found
Dim c4 = _
<db2:customer>Jim</>
End Sub
End Module
元素中声明的 XML 命名空间不适用于嵌入表达式中的 XML 文本。 例如:
' Error: Namespace prefix 'db' is not declared
Dim customer = _
<db:customer xmlns:db="http://example.org/database">
<%= <db:customer>Bob</> %>
</>
注意。 这是因为嵌入表达式可以是任何内容,包括函数调用。 如果函数调用包含 XML 文本表达式,则不清楚程序员是否希望应用或忽略 XML 命名空间。
XML 处理指令
XML 处理指令导致类型化为 System.Xml.Linq.XProcessingInstruction. XML 处理指令不能包含嵌入表达式,因为它们是处理指令中的有效语法。
XMLProcessingInstruction
: '<' '?' XMLProcessingTarget ( XMLWhitespace XMLProcessingValue? )? '?' '>'
;
XMLProcessingTarget
: '<Any XMLName except a casing permutation of the string "xml">'
;
XMLProcessingValue
: '<Any XMLString that does not contain a question-mark followed by ">">'
;
XML 注释
XML 注释导致类型化为 System.Xml.Linq.XComment. XML 注释不能包含嵌入的表达式,因为它们是注释中的有效语法。
XMLComment
: '<' '!' '-' '-' XMLCommentCharacter* '-' '-' '>'
;
XMLCommentCharacter
: '<Any XMLCharacter except dash (0x002D)>'
| '-' '<Any XMLCharacter except dash (0x002D)>'
;
CDATA 节
CDATA 节生成一个类型为 System.Xml.Linq.XCData. 的值。 CDATA 节不能包含嵌入的表达式,因为它们是 CDATA 节中的有效语法。
XMLCDATASection
: '<' '!' ( 'CDATA' '[' XMLCDATASectionString? ']' )? '>'
;
XMLCDATASectionString
: '<Any XMLString that does not contain the string "]]>">'
;
XML 成员访问表达式
XML 成员访问表达式访问 XML 值的成员。
XMLMemberAccessExpression
: Expression '.' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? IdentifierOrKeyword
| Expression '.' '.' '.' LineTerminator? '<' XMLQualifiedName '>'
;
有三种类型的 XML 成员访问表达式:
元素访问,其中 XML 名称遵循单个点。 例如:
Dim customer = _ <customer> <name>Bob</> </> Dim customerName = customer.<name>.Value元素访问映射到函数:
Function Elements(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XNode)因此,上述示例等效于:
Dim customerName = customer.Elements("name").Value属性访问,其中 Visual Basic 标识符遵循一个点和一个符号,或者 XML 名称跟在一个点和一个符号后面。 例如:
Dim customer = <customer age="30"/> Dim customerAge = customer.@age属性访问映射到函数:
Function AttributeValue(name As System.Xml.Linq.XName) as String因此,上述示例等效于:
Dim customerAge = customer.AttributeValue("age")注意。
AttributeValue扩展方法(以及相关的扩展属性Value)当前未在任何程序集中定义。 如果需要扩展成员,则会在生成的程序集中自动定义它们。子代访问,其中 XML 名称遵循三个点。 例如:
Dim company = _ <company> <customers> <customer>Bob</> <customer>Mary</> <customer>Joe</> </> </> Dim customers = company...<customer>后代访问映射到函数:
Function Descendents(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XElement)因此,上述示例等效于:
Dim customers = company.Descendants("customer")
XML 成员访问表达式的基表达式必须是一个值,并且必须属于以下类型:
如果元素或后代访问、
System.Xml.Linq.XContainer派生类型或System.Collections.Generic.IEnumerable(Of T)派生类型(其中TSystem.Xml.Linq.XContainer或派生类型)。如果属性访问、
System.Xml.Linq.XElement派生类型或System.Collections.Generic.IEnumerable(Of T)派生类型(其中TSystem.Xml.Linq.XElement或派生类型)。
XML 成员访问表达式中的名称不能为空。 可以使用导入定义的任何命名空间来限定命名空间。 例如:
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
Dim customer = _
<db:customer>
<db:name>Bob</>
</>
Dim name = customer.<db:name>
End Sub
End Module
XML 成员访问表达式中的 dot(s)之后或尖括号和名称之间不允许空格。 例如:
Dim customer = _
<customer age="30">
<name>Bob</>
</>
' All the following are error cases
Dim age = customer.@ age
Dim name = customer.< name >
Dim names = customer...< name >
如果命名空间中的 System.Xml.Linq 类型不可用,则 XML 成员访问表达式将导致编译时错误。
Await 运算符
await 运算符与 Async 方法中所述的异步方法相关。
AwaitOperatorExpression
: 'Await' Expression
;
Await 是保留字,如果立即封闭的方法或 lambda 表达式在其中显示有 Async 修饰符,如果 Await 出现在该 Async 修饰符之后,则为保留字;它在别处未保留。 预处理器指令中也未保留它。 await 运算符仅在方法或 lambda 表达式的正文中允许使用,其中它是保留字。 在立即封闭的方法或 lambda 中,await 表达式可能不会出现在语句CatchFinally的主体内、语句的主体SyncLock内或查询表达式内。
await 运算符采用一个表达式,该表达式必须分类为值,其类型必须是 可等待 的类型,或者 Object。 如果其类型是 Object ,则所有处理都会延迟到运行时。 如果以下所有内容 C 均属实,则类型将可等待:
C包含一个可访问的实例或扩展方法,GetAwaiter该方法没有参数,并且返回某些类型E;E包含一个可读的实例或扩展属性,IsCompleted该属性不采用任何参数,并且具有 boolean 类型;E包含一个可访问的实例或扩展方法,GetResult该方法不带任何参数;E实现或System.Runtime.CompilerServices.INotifyCompletionICriticalNotifyCompletion.
如果是 GetResult , Sub则 await 表达式被归类为 void。 否则,await 表达式被分类为值,其类型为方法的 GetResult 返回类型。
下面是可以等待的类的示例:
Class MyTask(Of T)
Function GetAwaiter() As MyTaskAwaiter(Of T)
Return New MyTaskAwaiter With {.m_Task = Me}
End Function
...
End Class
Structure MyTaskAwaiter(Of T)
Implements INotifyCompletion
Friend m_Task As MyTask(Of T)
ReadOnly Property IsCompleted As Boolean
Get
Return m_Task.IsCompleted
End Get
End Property
Sub OnCompleted(r As Action) Implements INotifyCompletion.OnCompleted
' r is the "resumptionDelegate"
Dim sc = SynchronizationContext.Current
If sc Is Nothing Then
m_Task.ContinueWith(Sub() r())
Else
m_Task.ContinueWith(Sub() sc.Post(Sub() r(), Nothing))
End If
End Sub
Function GetResult() As T
If m_Task.IsCanceled Then Throw New TaskCanceledException(m_Task)
If m_Task.IsFaulted Then Throw m_Task.Exception.InnerException
Return m_Task.Result
End Function
End Structure
注意。 建议库作者遵循在调用延续委托时SynchronizationContextOnCompleted调用其本身的模式。 此外,恢复委托不应在方法中 OnCompleted 同步执行,因为这可能会导致堆栈溢出:相反,委托应排队等待后续执行。
当控制流到达 Await 操作员时,行为如下所示。
GetAwaiter调用 await作数的方法。 此调用的结果称为 awaiter。检索 awaiter
IsCompleted的属性。 如果结果为 true,则:-
GetResult调用 awaiter 的方法。 如果GetResult为函数,则 await 表达式的值是此函数的返回值。
-
如果 IsCompleted 属性不为 true,则:
要么
ICriticalNotifyCompletion.UnsafeOnCompleted在 awaiter 上调用(如果 awaiter 的类型E实现ICriticalNotifyCompletion)或INotifyCompletion.OnCompleted(否则)。 在这两种情况下,它都会传递与异步方法的当前实例关联的 恢复委托 。当前异步方法实例的控制点已挂起,控制流在 当前调用方 (在节 异步方法中定义)中恢复。
如果稍后调用恢复委托,
- 恢复委托首先还原
System.Threading.Thread.CurrentThread.ExecutionContext到当时OnCompleted被调用的内容, - 然后,它会在异步方法实例的控制点恢复控制流(请参阅 Section Async Methods),
- 其中,它调用
GetResultawaiter 的方法,如上面的 2.1 所示。
- 恢复委托首先还原
如果 await作数的类型为 Object,则此行为将延迟到运行时:
- 步骤 1 是通过调用没有参数的 GetAwaiter() 来完成的;因此,它可以在运行时绑定到采用可选参数的实例方法。
- 步骤 2 是通过检索不带参数的 IsCompleted() 属性,并尝试将内部转换到布尔值来实现的。
- 步骤 3.a 是通过尝试
TryCast(awaiter, ICriticalNotifyCompletion)完成的,如果失败,则DirectCast(awaiter, INotifyCompletion)此作失败。
在 3.a 中传递的恢复委托只能调用一次。 如果多次调用该行为,则行为是未定义的。