给出以下VBA代码,假设Something
只是一个VBA类模块....
Public Type Foo
SomeThing As Something
End Type
Public Sub TestFoo()
Dim x As Foo
With x
'Correct way to do it
Set .someThing = New Something
End With
With x
'This is wrong but realized only as a RTE
'438: Object doesn't support this property or method
.SomeThing = New Something
End With
End Sub
相反,如果您将类型更改为VBA.Collection
,如下所示:
Public Type Foo
SomeThing As VBA.Collection
End Type
Public Sub TestFoo()
Dim x As Foo
With x
.SomeThing = New VBA.Collection
End With
End Sub
现在这是一个编译错误,使用Argument Not Optional
。这显然是错误的,但为什么它只是VBA.Collection
的编译时错误?
这在VBA语言规范中有解释。 With
块中赋值的语义是由语句是Set
语句还是Let
语句驱动的。在这种情况下,它是一个Let
声明:
With x
.SomeThing = New Something
End With
请注意,在VBA语法中,关键字Let
是可选的(已废弃):
let-statement = ["Let"] l-expression "=" expression
在Set
语句中,Set
关键字是必需的:
set-statement = "Set" l-expression "=" expression
在With
区块内部,l-expression
基本上是UDT成员,但如果直接使用x
,则完全相同的行为适用。
在评估Let
表达式时,语义在section 5.4.3.8中描述:
静态语义。
如果满足以下任何条件,则此语句无效:
- 无法将<expression>计算为简单数据值(第5.6.2.2节)。
接下来是5.6.2.2 (Evaluation to a simple data value),以下运行时语义适用(仅适用规则):
运行时语义。
在运行时,简单数据值的值和值类型是根据表达式的分类确定的,如下所示:
- 如果表达式的值类型是特定类: 如果源对象具有公共默认属性Get或公共默认函数,并且此默认成员的参数列表与包含0个参数的参数列表兼容,则简单数据值的值是将此默认成员作为简单数据值进行评估的结果。 否则,如果源对象没有公共默认属性Get或公共默认函数,则引发运行时错误438(对象不支持此属性或方法)。
因此SomeThing As Something
的运行时错误438。
使用Collection
,Let
静态语义仍然适用,但它无法使用5.6.2.2的静态语义(这给出了编译错误)。同样,省略了前面不适用的语义:
静态语义。可以评估以下类型的表达式以生成简单的数据值:
- 分类为值表达式的表达式可以基于以下规则评估为简单数据值: 如果表达式的声明类型是特定类: 如果此类具有公共默认属性Get或函数,并且此默认成员的参数列表与包含0个参数的参数列表兼容,则简单数据值评估将重新启动,就好像此默认成员是表达式一样。
Collection
的默认成员是一个函数(.Item
),它采用单个参数Index
。在此代码中,未提供参数,因此参数列表不兼容:
With x
.SomeThing = New VBA.Collection
End With
因此Argument Not Optional
编译错误。
在输入问题时,让我感到震惊的是VBA.Collection
有一个默认成员。因此,编译器将.Something = New VBA.Collection
解释为默认成员的赋值...除了Item
是索引属性。这就解释了为什么我们得到Argument not optional
这对于使用Set
语句来说是一个非常奇怪的错误。
相反,VBA类模块可能根本没有默认成员,因此没有索引属性+默认成员来触发编译时错误。但是,这也意味着在运行时才会捕获错误的语法。