【问题标题】:Why is VBA function giving compile error for collection but not for string为什么 VBA 函数会为集合提供编译错误,但不会为字符串提供编译错误
【发布时间】:2019-06-07 21:38:24
【问题描述】:

我正在尝试将集合传递给 VBA 函数,但出现编译错误,我似乎无法修复。

这是给出错误的简化示例。

Sub test()
    Dim fooString As String
    Dim fooCollection As collection
    Set fooCollection = New collection
    useString (fooString)
    useCollection (fooCollection)
End Sub

Public Function useString(foo As String)
    MsgBox ("here")
End Function

Public Function useCollection(foo As collection)
    MsgBox ("here")
End Function

我没有看到我正在做的事情与示例中所示的不同,例如此处显示的示例:How do I sort a collection?

这是我得到的错误(编译错误:参数不是可选的):

【问题讨论】:

    标签: excel vba


    【解决方案1】:

    由于隐含的“有用”默认成员,您会收到此对话框。在对象浏览器中导航到类窗格中的集合,在成员中,您将看到 Item 是默认成员,由蓝绿色图标指定。

    这是另一个例子。在运行代码之前问问自己它将打印什么并大声说出来。

    Public Sub DontWrapIt()
        Dim foo As Range
        Set foo = Sheet1.Range("A1")
        GiveMeARange foo
        GiveMeARange (foo)
    End Sub
    
    Private Sub GiveMeARange(ByVal param As Variant)
        Debug.Print TypeName(param)
    End Sub
    

    用括号包装对象会导致对其进行评估,这会导致隐式调用“有用的”默认成员...这需要未提供的Index 参数 - 因此,“参数不是可选的”。

    【讨论】:

      【解决方案2】:

      我不知道为什么会给出那个特定的错误(而不是更多信息的错误),但是当你调用一个 sub 或一个函数作为 sub (不使用返回值)时,你不应该有括号论据。以下作品:

      Sub test()
          Dim fooString As String
          Dim fooCollection As collection
          Set fooCollection = New collection
          useString fooString
          useCollection fooCollection
      End Sub
      
      Public Function useString(foo As String)
          MsgBox "here"
      End Function
      
      Public Function useCollection(foo As collection)
          MsgBox "here"
      End Function
      

      但是——如果你的函数没有真正返回任何东西,也许你可以将它们定义为 subs 开始。在 VBA 中,在像 C 这样的语言中创建一个 void 函数确实没有任何意义。

      【讨论】:

      • 您可以这样调用函数而无需赋值(并且没有编译错误),通常(您可以使用变体数组、范围、字符串、整数等来执行此操作)。我认为这里的错误可能与括号在以这种方式使用时如何强制对包含在其中的语句进行 evaluation 有关。集合没有值或默认值。
      • @DavidZemens 你几乎是正确的。 (fooCollection)trying 调用fooCollection.Item() 因为它是Collectiondefault method,而Item() 方法需要一个索引参数。缺少的是 那个 参数。
      • 此外,当您 New 收藏时,它最初是 Nothing。如果你选择Dim c as Object: Set c = New Collection,则不会出现编译错误(但会出现运行时450错误)
      • @DavidZemens 这是有道理的。括号通常是无害的(除非您有多个参数),但在这种情况下它们不是。 GSerg 确实解决了这个错误。
      • 参数周围的括号 always 强制评估为一个值,这具有强制传递参数ByVal 的副作用,即使过程表明它是ByRef 参数 - 我不同意这是“无害的”:它是一种静默的隐式操作,通常不会被注意到,直到它不可避免地导致问题......当你不知道怎么回事。
      【解决方案3】:

      只是调用另一个子的问题..

      如果它没有任何参数,你可以只用名字来调用它:

      useCollectionCall useCollection

      如果它确实有参数,那么你要么不带括号调用它,要么使用Call

      useCollection parameterCall useCollection(parameter)

      Sub test()
          Dim fooString As String
          Dim fooCollection As Collection
          Set fooCollection = New Collection
          useString (fooString)
      
          fooCollection.Add "test message", "test"
          useCollection fooCollection
          Call useCollection(fooCollection)
      End Sub
      
      Public Function useString(foo As String)
          MsgBox ("here")
      End Function
      
      Public Function useCollection(foo As Collection)
          MsgBox foo(1)
      End Function
      

      查看更多关于Call statement的信息:

      调用过程时不需要使用 Call 关键字。但是,如果您使用 Call 关键字调用需要参数的过程,则 argumentlist 必须用括号括起来。如果省略 Call 关键字,则还必须省略 argumentlist 周围的括号。如果您使用 Call 语法来调用任何内部或用户定义的函数,则该函数的返回值将被丢弃

      【讨论】:

      • 不要使用Call - 它已被弃用。这意味着不再受支持并且可能随时从语言中消失。为什么要创建具有已死功能的新代码?
      • @AJD 在提到 Call 已被弃用之前,我见过人们,但在文档站点上并没有提到被弃用。您能否提供一些资源来支持您的主张?我也更新了我的答案,并从文档页面提供了更多详细信息。
      • @FAB In QuickBasic/QBasic for Dos Call 允许调用汇编函数。对于汇编函数,由于某种技术原因,它不能返回值。所以调用丢弃返回值。它使用它来将变量作为参数传递,然后填充变量。传入的参数必须足够大(否则会崩溃)以保存返回值。它在 Windows 中什么都不做,除了与旧的 Dos 代码兼容,以防你移植它。
      • @Noodles 感谢您对 QuickBasic/QBasic 的深入了解,老实说,这是我第一次听说它们,所以这对我来说听起来像是一堂历史课。在今天阅读上述文档之前,我并不具体知道 Call 会丢弃值,但如果您是 Calling(而不是分配给变量),那么您无论如何都不会期望返回值,所以有点隐含明白什么都不会退还。至于Call 声明,它确实使代码更具可读性(对我而言),而且据我所知,它并没有被弃用……想知道人们为什么这么说。
      • @Fab 呼叫的设计如此说func(Param1 as long, Param2 as string) as Anything。所以 Param2 是函数返回值的地方,但是函数不能分配内存,所以 Param2 必须足够大以在传递它之前保存返回值。此外,Basic 的一个内在特性是,如果您不关心调用 Function 的返回值,就好像它是 Sub 一样。这就是 Basic 程序员对您的期望,以便他们能够理解您的代码。
      【解决方案4】:

      这行得通:

      Sub test()
          Dim fooString As String
          Dim fooCollection As VBA.collection
          Set fooCollection = New VBA.collection
          fooCollection.Add "bar", "bar"
          useString (fooString)
          useCollection fooCollection
      End Sub
      
      Public Function useString(foo As String)
          MsgBox ("useString")
      End Function
      
      Public Function useCollection(ByRef foo As VBA.collection)
          MsgBox ("useCollection")
      End Function
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-17
        • 1970-01-01
        • 2015-02-08
        • 2014-06-08
        • 1970-01-01
        • 2016-09-02
        • 2017-01-19
        相关资源
        最近更新 更多