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 gegeben foo 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, weil DoSomething vor dem Zurückgeben einen Verweis auf den Objektzeiger erhalten und diesem Nothing 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 gegeben foo 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änge DoSomething gegeben foo und erhält eine Kopie des Zeigers auf die Collection Objekt. Da die foo Objektvariable in den Test - Umfang Punkte auf das gleiche Objekt, ein Element in das Hinzufügen DoSomething fügt das Element auf das gleiche Objekt. Da es sich um eine Kopie des Zeigers handelt, wirkt sich die Einstellung des Verweises auf Nothing nicht auf die eigene Kopie des Aufrufers aus.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow