VBA
Passando argomenti ByRef o ByVal
Ricerca…
introduzione
I modificatori ByRef
e ByVal
fanno parte della firma di una procedura e indicano come viene passato un argomento a una procedura. In VBA un parametro è passato a ByRef
se non diversamente specificato (es. ByRef
è implicito se assente).
Nota In molti altri linguaggi di programmazione (incluso VB.NET), i parametri vengono passati implicitamente dal valore se non viene specificato alcun modificatore: considerare di specificare esplicitamente i modificatori ByRef
per evitare possibili confusioni.
Osservazioni
Passare gli array
Le matrici devono essere passate per riferimento. Questo codice viene compilato, ma solleva l'errore di run-time 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
Questo codice non viene compilato:
Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
foo.Add 42
End Sub
Passare variabili semplici ByRef e ByVal
Passando ByRef
o ByVal
indica se il valore effettivo di un argomento viene passato a CalledProcedure
da CallingProcedure
o se un riferimento (chiamato un puntatore in alcuni altri linguaggi) viene passato a CalledProcedure
.
Se viene passato un argomento ByRef
, l'indirizzo di memoria dell'argomento viene passato a CalledProcedure
e qualsiasi modifica a tale parametro da CalledProcedure
viene apportata al valore in CallingProcedure
.
Se un argomento viene passato a ByVal
, il valore effettivo, non un riferimento alla variabile, viene passato a CalledProcedure
.
Un semplice esempio illustrerà chiaramente questo:
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 altro esempio:
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
Modificatore predefinito
Se non è specificato alcun modificatore per un parametro, quel parametro viene passato implicitamente per riferimento.
Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub
Il parametro foo
viene passato da ByRef
in DoSomething1
e DoSomething2
.
Attento! Se vieni in VBA con esperienza in altre lingue, è molto probabile che sia esattamente il contrario di quello a cui sei abituato. In molti altri linguaggi di programmazione (incluso VB.NET), il modificatore implicito / predefinito passa i parametri per valore.
Passando per riferimento
Quando un valore viene passato
ByRef
, la procedura riceve un riferimento al valore.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
Richiamo delle uscite della procedura
Test
sopra 84.DoSomething
viene assegnato afoo
e riceve un riferimento al valore e pertanto funziona con lo stesso indirizzo di memoria del chiamante.Quando viene passato un riferimento
ByRef
, la procedura riceve un riferimento al puntatore.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
Il codice precedente solleva l' errore 91 di runtime , poiché il chiamante chiama il membro
Count
di un oggetto che non esiste più, perché aDoSomething
stato assegnato un riferimento al puntatore dell'oggetto e lo ha assegnato aNothing
prima di tornare.
Forcing ByVal sul sito di chiamata
Utilizzando le parentesi sul sito di chiamata, è possibile ignorare ByRef
e forzare un argomento a passare 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
Il codice precedente emette 42, indipendentemente dal fatto che ByRef
sia specificato in modo implicito o esplicito.
Attento! Per questo motivo, l'uso di parentesi estranee nelle chiamate di procedura può facilmente introdurre bug. Presta attenzione agli spazi bianchi tra il nome della procedura e l'elenco degli argomenti:
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
Passando per valore
Quando un valore viene passato a
ByVal
, la procedura riceve una copia del valore.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
Chiamando le uscite della procedura
Test
sopra descritte 42.DoSomething
viene assegnato afoo
e riceve una copia del valore. La copia viene moltiplicata per 2 e quindi scartata quando la procedura termina; la copia del chiamante non è mai stata modificata.Quando viene passato un riferimento
ByVal
, la procedura riceve una copia del puntatore.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
Richiamo delle uscite della procedura
Test
sopra 1.DoSomething
viene fornitofoo
e riceve una copia del puntatore sull'oggettoCollection
. Poiché la variabile oggettofoo
nell'ambitoTest
punta allo stesso oggetto, l'aggiunta di un elemento inDoSomething
aggiunge l'elemento allo stesso oggetto. Poiché si tratta di una copia del puntatore, l'impostazione del suo riferimento aNothing
non influisce sulla copia del chiamante.