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
ByRefprocedura 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 SubWywołanie powyższej procedury
Testpowoduje wyjście 84.DoSomethingotrzymujefooi 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 SubPowyższy kod podnosi błąd czasu wykonania 91 , ponieważ program wywołujący wywołuje element
Countobiektu, który już nie istnieje, ponieważDoSomethingotrzymał odwołanie do wskaźnika obiektu i przypisał go doNothingprzed 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
ByValprocedura 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 SubWywołanie powyższej procedury
Testdaje wynik 42.DoSomethingotrzymujefooi 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
ByValprocedura 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 SubWywołanie powyższej procedury
Testpowoduje wyjście 1.DoSomethingotrzymujefooi otrzymuje kopię wskaźnika do obiektuCollection. Ponieważ zmienna obiektufoow zakresieTestwskazuje na ten sam obiekt, dodanie elementu wDoSomethingpowoduje dodanie elementu do tego samego obiektu. Ponieważ jest to kopia wskaźnika, ustawienie jej odniesienia naNothingnie wpływa na własną kopię osoby dzwoniącej.