Szukaj…


Wprowadzenie

Modyfikatory ByRef i ByVal są częścią podpisu procedury i wskazują, w jaki sposób argument jest przekazywany do procedury. W VBA parametr jest przekazywany ByRef chyba że określono inaczej (tzn. ByRef jest domyślny, jeśli nieobecny).

Uwaga W wielu innych językach programowania (w tym VB.NET) parametry są domyślnie przekazywane przez wartość, jeśli nie określono żadnego modyfikatora: rozważ jawne podanie modyfikatorów ByRef aby uniknąć możliwego pomyłki.

Uwagi

Przekazywanie tablic

Tablice muszą być przekazywane przez odniesienie. Ten kod się kompiluje, ale podnosi błąd 424 „Wymagany obiekt”:

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

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

Ten kod nie kompiluje:

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

Przekazywanie prostych zmiennych ByRef i ByVal

Przekazanie ByRef lub ByVal wskazuje, czy rzeczywista wartość argumentu jest przekazywana do CalledProcedure przez CallingProcedure , czy też odwołanie (nazywane wskaźnikiem w niektórych innych językach) jest przekazywane do CalledProcedure .

Jeśli argument zostanie przekazany przez ByRef , adres pamięci argumentu jest przekazywany do procedury CalledProcedure a wszelkie modyfikacje tego parametru przez procedurę CalledProcedure są wprowadzane do wartości w procedurze CallingProcedure .

Jeśli argument zostanie przekazany przez ByVal , rzeczywista wartość, a nie odwołanie do zmiennej, jest przekazywana do procedury CalledProcedure .

Prosty przykład zilustruje to wyraźnie:

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

Inny przykład:

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


Domyślny modyfikator

Jeśli dla parametru nie podano modyfikatora, parametr ten jest domyślnie przekazywany przez odwołanie.

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

Parametr foo jest przekazywany ByRef zarówno w DoSomething1 jak i DoSomething2 .

Uważaj! Jeśli przyjeżdżasz do VBA z doświadczeniem z innych języków, jest to bardzo dokładnie odwrotne zachowanie niż to, do którego przywykłeś. W wielu innych językach programowania (w tym VB.NET) niejawny / domyślny modyfikator przekazuje parametry według wartości.


Przekazywanie przez odniesienie

  • Po przekazaniu wartości ByRef procedura otrzymuje odniesienie do wartości.

    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
    

    Wywołanie powyższej procedury Test powoduje wyjście 84. DoSomething otrzymuje foo i otrzymuje odniesienie do wartości, a zatem działa z tym samym adresem pamięci co wywołujący.

  • Gdy referencja jest przekazywana ByRef , procedura odbiera referencję do wskaźnika.

    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
    

    Powyższy kod podnosi błąd czasu wykonania 91 , ponieważ program wywołujący wywołuje element Count obiektu, który już nie istnieje, ponieważ DoSomething otrzymał odwołanie do wskaźnika obiektu i przypisał go do Nothing przed powrotem.


Wymuszanie ByVal na stronie połączenia

Używając nawiasów w witrynie wywoływania, możesz zastąpić ByRef i zmusić do przekazania argumentu 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

Powyższy kod wyprowadza 42, niezależnie od tego, czy ByRef jest określony pośrednio czy jawnie.

Uważaj! Z tego powodu używanie obcych nawiasów w wywołaniach procedur może z łatwością wprowadzać błędy. Zwróć uwagę na spację między nazwą procedury a listą argumentów:

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

Przekazywanie według wartości

  • Po przekazaniu wartości ByVal procedura otrzymuje kopię wartości.

    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
    

    Wywołanie powyższej procedury Test daje wynik 42. DoSomething otrzymuje foo i otrzymuje kopię wartości. Kopia jest mnożona przez 2, a następnie odrzucana po zakończeniu procedury; kopia dzwoniącego nigdy nie została zmieniona.

  • Po przekazaniu odwołania ByVal procedura otrzymuje kopię wskaźnika.

    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
    

    Wywołanie powyższej procedury Test powoduje wyjście 1. DoSomething otrzymuje foo i otrzymuje kopię wskaźnika do obiektu Collection . Ponieważ zmienna obiektu foo w zakresie Test wskazuje na ten sam obiekt, dodanie elementu w DoSomething powoduje dodanie elementu do tego samego obiektu. Ponieważ jest to kopia wskaźnika, ustawienie jej odniesienia na Nothing nie wpływa na własną kopię osoby dzwoniącej.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow