VBA
Argumente übergeben ByRef oder ByVal
Suche…
Einführung
Die Modifizierer ByRef
und ByVal
sind Teil der Signatur einer Prozedur und geben an, wie ein Argument an eine Prozedur übergeben wird. In VBA wird ein Parameter an ByRef
sofern nichts anderes angegeben ist (dh ByRef
ist implizit, falls nicht vorhanden).
Hinweis In vielen anderen Programmiersprachen (einschließlich VB.NET) werden Parameter implizit durch einen Wert übergeben, wenn kein Modifikator angegeben ist. Sie ByRef
explizit ByRef
Modifikatoren angeben, um mögliche Verwirrungen zu vermeiden.
Bemerkungen
Arrays übergeben
Arrays müssen als Referenz übergeben werden. Dieser Code wird kompiliert, verursacht jedoch den Laufzeitfehler 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
Dieser Code kompiliert nicht:
Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
foo.Add 42
End Sub
Einfache Variablen übergeben ByRef und ByVal
Übergeben von ByRef
oder ByVal
gibt an, ob der tatsächliche Wert eines Arguments von CalledProcedure
an CallingProcedure
wird oder ob eine Referenz (in anderen Sprachen als Zeiger bezeichnet) an CalledProcedure
.
Wenn ein Argument ByRef
, wird die Speicheradresse des Arguments an die CalledProcedure
und alle Änderungen an diesem Parameter durch die CalledProcedure
an dem Wert in der CallingProcedure
.
Wenn ein Argument ByVal
, wird der tatsächliche Wert, nicht ein Verweis auf die Variable, an die CalledProcedure
.
Ein einfaches Beispiel veranschaulicht dies deutlich:
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
Ein anderes Beispiel:
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
Standardmodifikator
Wenn für einen Parameter kein Modifikator angegeben ist, wird dieser Parameter implizit als Referenz übergeben.
Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub
Der foo
Parameter übergeben wird ByRef
sowohl in DoSomething1
und DoSomething2
.
Achtung! Wenn Sie mit Erfahrungen aus anderen Sprachen zu VBA kommen, ist dies sehr wahrscheinlich das genaue Gegenteil von dem, das Sie gewohnt sind. In vielen anderen Programmiersprachen (einschließlich VB.NET) übergibt der implizite / default-Modifikator Parameter nach Wert.
Übergabe als Referenz
Wenn ein Wert
ByRef
, erhält die Prozedur einen Verweis auf den Wert.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
Der Aufruf der oben
Test
- Prozedur Ausgänge 84.DoSomething
ist gegebenfoo
und erhält einen Verweis auf den Wert, und deshalb arbeitet mit der gleichen Speicheradresse wie der Aufrufer.Wenn eine Referenz
ByRef
, erhält die Prozedur eine Referenz auf den Zeiger.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
Der obige Code verursacht einen Laufzeitfehler 91 , da der Aufrufer das
Count
DoSomething
eines Objekts aufruft, das nicht mehr vorhanden ist, weilDoSomething
vor dem Zurückgeben einen Verweis auf den Objektzeiger erhalten und diesemNothing
zugewiesen hat.
ByVal am Anrufort erzwingen
Mit Klammern an der Aufrufstelle können Sie ByRef
überschreiben und die ByRef
eines Arguments für 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
Der obige Code gibt 42 aus, unabhängig davon, ob ByRef
implizit oder explizit angegeben wird.
Achtung! Aus diesem Grund kann die Verwendung fremder Klammern in Prozeduraufrufen leicht zu Fehlern führen. Achten Sie auf den Leerraum zwischen dem Prozedurnamen und der Argumentliste:
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
Übergabe nach Wert
Wenn ein Wert
ByVal
, erhält die Prozedur eine Kopie des Werts.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
Der Aufruf der oben
Test
- Prozedur Ausgänge 42.DoSomething
ist gegebenfoo
und erhält eine Kopie des Wertes. Die Kopie wird mit 2 multipliziert und dann verworfen, wenn die Prozedur beendet wird. Die Kopie des Anrufers wurde nie geändert.Wenn eine Referenz
ByVal
, erhält die Prozedur eine Kopie des Zeigers.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
Aufrufen der obigen
Test
- Verfahren 1. AusgängeDoSomething
gegebenfoo
und erhält eine Kopie des Zeigers auf dieCollection
Objekt. Da diefoo
Objektvariable in denTest
- Umfang Punkte auf das gleiche Objekt, ein Element in das HinzufügenDoSomething
fügt das Element auf das gleiche Objekt. Da es sich um eine Kopie des Zeigers handelt, wirkt sich die Einstellung des Verweises aufNothing
nicht auf die eigene Kopie des Aufrufers aus.