Поиск…


Вступление

Модификаторы ByRef и ByVal являются частью сигнатуры процедуры и указывают, как передается аргумент процедуре. В VBA параметр передается ByRef если не указано иное (т.е. ByRef неявно, если отсутствует).

Примечание. Во многих других языках программирования (включая VB.NET) параметры неявно передаются по значению, если не указан модификатор: подумайте о том, чтобы указать модификаторы ByRef явно, чтобы избежать возможной путаницы.

замечания

Передача массивов

Массивы должны передаваться по ссылке. Этот код компилируется, но вызывает ошибку времени выполнения 424 «Object Required»:

Public Sub Test()
    DoSomething Array(1, 2, 3)
End Sub

Private Sub DoSomething(ByVal foo As Variant)
    foo.Add 42
End Sub

Этот код не компилируется:

Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
    foo.Add 42
End Sub

Передача простых переменных ByRef And ByVal

Передача ByRef или ByVal указывает, ByVal ли фактическое значение аргумента CalledProcedure CallingProcedure , или же ссылка (называемая указателем на некоторых других языках) передается CalledProcedure .

Если аргумент передан ByRef , адрес памяти аргумента передается в CalledProcedure и любая модификация этого параметра CalledProcedure выполняется в значение CallingProcedure .

Если аргумент передается ByVal , фактическое значение, а не ссылка на переменную, передается в CalledProcedure .

Простой пример будет ясно иллюстрировать это:

Sub CalledProcedure(ByRef X As Long, ByVal Y As Long)
    X = 321
    Y = 654
End Sub

Sub CallingProcedure()
    Dim A As Long
    Dim B As Long
    A = 123
    B = 456

    Debug.Print "BEFORE CALL => A: " & CStr(A), "B: " & CStr(B)
    ''Result : BEFORE CALL => A: 123 B: 456

    CalledProcedure X:=A, Y:=B

    Debug.Print "AFTER CALL =  A: " & CStr(A), "B: " & CStr(B)
    ''Result : AFTER CALL => A: 321 B: 456
End Sub

Другой пример:

Sub Main()
    Dim IntVarByVal As Integer
    Dim IntVarByRef As Integer
    
    IntVarByVal = 5
    IntVarByRef = 10
    
    SubChangeArguments IntVarByVal, IntVarByRef '5 goes in as a "copy". 10 goes in as a reference
    Debug.Print "IntVarByVal: " & IntVarByVal 'prints 5 (no change made by SubChangeArguments)
    Debug.Print "IntVarByRef: " & IntVarByRef 'prints 99 (the variable was changed in SubChangeArguments)
End Sub

Sub SubChangeArguments(ByVal ParameterByVal As Integer, ByRef ParameterByRef As Integer)
    ParameterByVal = ParameterByVal + 2 ' 5 + 2 = 7 (changed only inside this Sub)
    ParameterByRef = ParameterByRef + 89 ' 10 + 89 = 99 (changes the IntVarByRef itself - in the Main Sub)
End Sub

ByRef


Модификатор по умолчанию

Если для параметра не указан модификатор, этот параметр неявно передается по ссылке.

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

Параметр foo передается ByRef как в DoSomething1 и DoSomething2 .

Осторожно! Если вы приходите в 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

Вышеуказанные выходы кода 42, независимо от того, указан ли ByRef неявно или явно.

Осторожно! Из-за этого, используя посторонние круглые скобки в процедурных вызовах, можно легко ввести ошибки. Обратите внимание на пробелы между именем процедуры и списком аргументов:

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

ByVal

Передача по значению

  • Когда значение передается ByVal , процедура получает копию значения.

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

    Вызов вышеуказанных выходов Test процедуры 42. DoSomething присваивается foo и получает копию значения. Копия умножается на 2, а затем отменяется, когда процедура завершается; копия вызывающего абонента никогда не изменялась.

  • Когда ссылка передается ByVal , процедура получает копию указателя.

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

    Вызов вышеуказанных результатов Test процедуры 1. DoSomething предоставляется foo и получает копию указателя на объект Collection . Поскольку переменная объекта foo в области Test указывает на один и тот же объект, добавление элемента в DoSomething добавляет элемент к одному и тому же объекту. Поскольку это копия указателя, установка ссылки на Nothing не влияет на собственную копию вызывающего абонента.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow