为什么某些类型缺少`Set`会导致运行时错误而不是编译错误?

时间:2018-08-30 04:27:50

标签: vba

给出以下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会导致编译时错误?

2 个答案:

答案 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类模块可能根本没有默认成员,因此没有索引属性+默认成员来触发编译时错误。但是,这也意味着不良语法要到运行时才能被发现。