为 ByRef

默认修饰符

如果没有为参数指定修饰符,则通过引用隐式传递该参数。

Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub

foo 参数在 DoSomething1DoSomething2 中传递 ByRef

小心! 如果你带着其他语言的经验来到 VBA,这很可能与你习惯的完全相反。在许多其他编程语言(包括 VB.NET)中,隐式/默认修饰符按值传递参数。

通过引用传递

  • 传递 ByRef 时,过程接收对值的引用

    Public Sub Test()
        Dim foo As Long
        foo = 42
        DoSomething foo
        Debug.Print foo
    End Sub
    
    Private Sub DoSomething(ByRef foo As Long)
        foo = foo * 2
    End Sub
    

    调用上面的 Test 过程输出 84. DoSomething 被赋予 foo 并接收对该值的引用,因此使用与调用者相同的内存地址。

  • 参考被传递 ByRef,程序接收一个参考指针。

    Public Sub Test()
        Dim foo As Collection
        Set foo = New Collection
        DoSomething foo
        Debug.Print foo.Count
    End Sub
    
    Private Sub DoSomething(ByRef foo As Collection)
        foo.Add 42
        Set foo = Nothing
    End Sub
    

    上面的代码引发了运行时错误 91 ,因为调用者正在调用不再存在的对象的 Count 成员,因为 DoSomething 被赋予对象指针的引用并在返回之前将其分配给 Nothing

在呼叫站点强制 ByVal

在呼叫站点使用括号,你可以覆盖 ByRef 并强制传递参数 ByVal

Public Sub Test()
    Dim foo As Long
    foo = 42
    DoSomething (foo)
    Debug.Print foo
End Sub

Private Sub DoSomething(ByRef foo As Long)
    foo = foo * 2
End Sub

无论是隐式还是显式指定 ByRef,上述代码都输出 42。

小心! 因此,在过程调用中使用无关的括号很容易引入错误。注意过程名称和参数列表之间的空格:

bar = `DoSomething(foo)` 'function call, no whitespace; parens are part of args list
DoSomething (foo) 'procedure call, notice whitespace; parens are NOT part of args list
DoSomething foo 'procedure call does not force the foo parameter to be ByVal