VBA
Przekazywanie argumentów ByRef lub ByVal
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
otrzymujefoo
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 doNothing
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
otrzymujefoo
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
otrzymujefoo
i otrzymuje kopię wskaźnika do obiektuCollection
. Ponieważ zmienna obiektufoo
w zakresieTest
wskazuje na ten sam obiekt, dodanie elementu wDoSomething
powoduje dodanie elementu do tego samego obiektu. Ponieważ jest to kopia wskaźnika, ustawienie jej odniesienia naNothing
nie wpływa na własną kopię osoby dzwoniącej.