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 SubL' appel ci - dessus
Testdes sorties de procédure 84.DoSomethingest donnéfooet 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 SubLe code ci-dessus génère l' erreur d'exécution 91 , car l'appelant appelle le membre
Countd'un objet qui n'existe plus, carDoSomethinga reçu une référence au pointeur d'objet et lui a attribué la valeurNothingavant 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 SubAppeler les
Testprocédure deTestci-dessus 42.DoSomethingse voit attribuer la valeurfooet 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 SubAppel des
Testprocédure deTestci-dessus 1.DoSomethingse voit attribuer la valeurfooet reçoit une copie du pointeur sur l'objetCollection. Étant donné que la variable d'objetfoodans la portéeTestpointe vers le même objet, l'ajout d'un élément dansDoSomethingajoute l'élément au même objet. Comme il s'agit d' une copie du pointeur, définir sa référence àNothingn'affecte pas la copie de l'appelant.