给出以下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
会导致编译时错误?
答案 0 :(得分:3)
这在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中进行了描述:
静态语义。
如果满足以下任一条件,则此语句无效:
不能求值为简单数据值(第 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的 static 语义(产生编译错误)。同样,省略了前面不适用的语义:
静态语义。可以对以下类型的表达式求值以生成简单的数据值:
根据以下规则,可以将分类为值表达式的表达式评估为简单数据值:
如果表达式的声明类型是特定类:
如果此类具有公共的默认属性Get或函数,并且此默认成员的参数列表与参数兼容 包含0个参数的列表,简单的数据值评估重新开始 如果此默认成员是表达式。
Collection
的默认成员是带有单个参数.Item
的函数(Index
)。在此代码中,未提供参数,因此参数列表不兼容:
With x
.SomeThing = New VBA.Collection
End With
因此出现Argument Not Optional
编译错误。
答案 1 :(得分:1)
输入问题时,令我惊讶的是VBA.Collection
具有默认成员。因此,编译器将.Something = New VBA.Collection
解释为默认成员的 assignment ...,除了Item
是一个索引属性。这就解释了为什么我们得到Argument not optional
的原因,而使用Set
语句却是一个很奇怪的错误。
相反,VBA类模块可能根本没有默认成员,因此没有索引属性+默认成员来触发编译时错误。但是,这也意味着不良语法要到运行时才能被发现。