VBA
Argumenten doorgeven ByRef of ByVal
Zoeken…
Invoering
De ByRef
en ByVal
maken deel uit van de handtekening van een procedure en geven aan hoe een argument wordt doorgegeven aan een procedure. In VBA wordt een parameter doorgegeven aan ByRef
tenzij anders aangegeven (dwz ByRef
is impliciet indien afwezig).
Opmerking In veel andere programmeertalen (inclusief VB.NET) worden parameters impliciet doorgegeven als er geen modifier is opgegeven: overweeg om ByRef
modifiers expliciet op te geven om mogelijke verwarring te voorkomen.
Opmerkingen
Arrays passeren
Arrays moeten worden doorgegeven door middel van verwijzing. Deze code wordt gecompileerd, maar veroorzaakt runtime-fout 424 "Object vereist":
Public Sub Test()
DoSomething Array(1, 2, 3)
End Sub
Private Sub DoSomething(ByVal foo As Variant)
foo.Add 42
End Sub
Deze code bestaat niet uit:
Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
foo.Add 42
End Sub
Eenvoudige variabelen doorgeven ByRef en ByVal
Passing ByRef
of ByVal
geeft aan of de werkelijke waarde van een argument door de CallingProcedure
aan de CalledProcedure
wordt doorgegeven, of dat een verwijzing (in sommige andere talen een pointer genoemd) wordt doorgegeven aan de CalledProcedure
.
Als een argument door ByRef
wordt doorgegeven, wordt het geheugenadres van het argument doorgegeven aan de CalledProcedure
en wordt elke wijziging in die parameter door de CalledProcedure
aangebracht aan de waarde in de CallingProcedure
.
Als een argument wordt doorgegeven ByVal
, wordt de werkelijke waarde, niet een verwijzing naar de variabele, doorgegeven aan de CalledProcedure
.
Een eenvoudig voorbeeld zal dit duidelijk illustreren:
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
Een ander voorbeeld:
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
Standaard modifier
Als er geen modifier is opgegeven voor een parameter, wordt die parameter impliciet doorgegeven als referentie.
Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub
De parameter foo
wordt doorgegeven ByRef
in zowel DoSomething1
als DoSomething2
.
Kijk uit! Als je naar VBA komt met ervaring uit andere talen, is dit waarschijnlijk precies het tegenovergestelde gedrag dan je gewend bent. In veel andere programmeertalen (inclusief VB.NET) geeft de impliciete / standaard modifier parameters door aan waarde.
Doorgeven door referentie
Wanneer een waarde aan
ByRef
wordt doorgegeven, ontvangt de procedure een verwijzing naar de waarde.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
Roept de bovenstaande
Test
procedure uitvoert 84.DoSomething
gegevenfoo
en een verwijzing naar de waarde ontvangt en dus werkt met hetzelfde geheugenadres als de beller.Wanneer een referentie aan
ByRef
wordt doorgegeven, ontvangt de procedure een verwijzing naar de aanwijzer.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
De bovenstaande code roept runtime-fout 91 op , omdat de beller het
Count
lid van een object oproept dat niet langer bestaat, omdatDoSomething
een verwijzing naar deDoSomething
gekregen en deze aanNothing
toegewezen voordat deze terugkeerde.
ByVal forceren op oproepsite
Met haakjes op de oproepsite kunt u ByRef
overschrijven en een argument dwingen 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
De bovenstaande code voert 42 uit, ongeacht of ByRef
impliciet of expliciet is gespecificeerd.
Kijk uit! Daarom kan het gebruik van vreemde haakjes bij procedureaanroepen gemakkelijk bugs introduceren. Let op de witruimte tussen de procedurenaam en de lijst met argumenten:
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
Waarde doorgeven
Wanneer een waarde wordt doorgegeven
ByVal
, ontvangt de procedure een kopie van de waarde.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
Roept de bovenstaande
Test
procedure uitvoert 42.DoSomething
gegevenfoo
en een kopie van de waarde ontvangt. De kopie wordt vermenigvuldigd met 2 en vervolgens verwijderd wanneer de procedure wordt afgesloten; het exemplaar van de beller is nooit gewijzigd.Wanneer een verwijzing door
ByVal
wordt doorgegeven, ontvangt de procedure een kopie van de aanwijzer.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
Roept de bovenstaande
Test
procedure uitvoert 1.DoSomething
gegevenfoo
en ontvangt een kopie van de wijzer naar hetCollection
object. Omdat defoo
objectvariabele in deTest
bereik verwijst naar hetzelfde object, het toevoegen van een objectDoSomething
voegt de punt naar hetzelfde object. Omdat het een kopie van de aanwijzer is, heeft het instellen van de verwijzing naarNothing
geen invloed op de kopie van de beller.