VBA
Arguments de passage ByRef ou ByVal
Recherche…
Introduction
Les modificateurs ByRef
et ByVal
font partie de la signature d'une procédure et indiquent comment un argument est transmis à une procédure. Dans VBA, un paramètre est transmis à ByRef
sauf indication contraire (c'est-à-dire que ByRef
est implicite s'il est absent).
Remarque Dans de nombreux autres langages de programmation (y compris VB.NET), les paramètres sont implicitement passés par valeur si aucun modificateur n'est spécifié: envisagez de spécifier explicitement les modificateurs ByRef
pour éviter toute confusion possible.
Remarques
Tableaux passants
Les tableaux doivent être transmis par référence. Ce code compile, mais déclenche l'erreur d'exécution 424 "Objet requis":
Public Sub Test()
DoSomething Array(1, 2, 3)
End Sub
Private Sub DoSomething(ByVal foo As Variant)
foo.Add 42
End Sub
Ce code ne compile pas:
Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
foo.Add 42
End Sub
Passer des variables simples ByRef et ByVal
Passage ByRef
ou ByVal
indique si la valeur réelle d'un argument est transmise à CalledProcedure
par CallingProcedure
ou si une référence (appelée pointeur dans d'autres langues) est transmise à CalledProcedure
.
Si un argument est transmis à ByRef
, l'adresse mémoire de l'argument est transmise à CalledProcedure
et toute modification de ce paramètre par CalledProcedure
est apportée à la valeur de la CallingProcedure
.
Si un argument est transmis à ByVal
, la valeur réelle, et non une référence à la variable, est transmise à la CalledProcedure
.
Un exemple simple illustrera cela clairement:
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
Un autre exemple:
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
Modificateur par défaut
Si aucun modificateur n'est spécifié pour un paramètre, ce paramètre est implicitement transmis par référence.
Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub
Le paramètre foo
est transmis à ByRef
à la fois dans DoSomething1
et DoSomething2
.
Fais attention! Si vous venez à VBA avec l'expérience d'autres langues, c'est probablement le comportement opposé à celui auquel vous êtes habitué. Dans de nombreux autres langages de programmation (y compris VB.NET), le modificateur implicite / default transmet les paramètres par valeur.
En passant par référence
Lorsqu'une valeur est transmise par
ByRef
, la procédure reçoit une référence à la valeur.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
L' appel ci - dessus
Test
des sorties de procédure 84.DoSomething
est donnéfoo
et reçoit une référence à la valeur, et travaille donc avec la même adresse de mémoire que l'appelant.Lorsqu'une référence est transmise par
ByRef
, la procédure reçoit une référence au pointeur.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
Le code ci-dessus génère l' erreur d'exécution 91 , car l'appelant appelle le membre
Count
d'un objet qui n'existe plus, carDoSomething
a reçu une référence au pointeur d'objet et lui a attribué la valeurNothing
avant le renvoi.
Forçage de ByVal sur le site d'appel
En utilisant des parenthèses sur le site d'appel, vous pouvez remplacer ByRef
et forcer le passage d'un argument 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
Le code ci-dessus produit 42, que ByRef
soit spécifié implicitement ou explicitement.
Fais attention! De ce fait, l'utilisation de parenthèses superflues dans les appels de procédure peut facilement introduire des bogues. Faites attention aux espaces entre le nom de la procédure et la liste des arguments:
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
En passant par la valeur
Lorsqu'une valeur est transmise par
ByVal
, la procédure reçoit une copie de la valeur.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
Appeler les
Test
procédure deTest
ci-dessus 42.DoSomething
se voit attribuer la valeurfoo
et reçoit une copie de la valeur. La copie est multipliée par 2, puis supprimée lorsque la procédure est terminée; la copie de l'appelant n'a jamais été modifiée.Lorsqu'une référence est transmise à
ByVal
, la procédure reçoit une copie du pointeur.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
Appel des
Test
procédure deTest
ci-dessus 1.DoSomething
se voit attribuer la valeurfoo
et reçoit une copie du pointeur sur l'objetCollection
. Étant donné que la variable d'objetfoo
dans la portéeTest
pointe vers le même objet, l'ajout d'un élément dansDoSomething
ajoute l'élément au même objet. Comme il s'agit d' une copie du pointeur, définir sa référence àNothing
n'affecte pas la copie de l'appelant.