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 a foo 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é a DoSomething stato assegnato un riferimento al puntatore dell'oggetto e lo ha assegnato a Nothing 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 a foo 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 fornito foo e riceve una copia del puntatore sull'oggetto Collection . Poiché la variabile oggetto foo nell'ambito Test punta allo stesso oggetto, l'aggiunta di un elemento in DoSomething aggiunge l'elemento allo stesso oggetto. Poiché si tratta di una copia del puntatore, l'impostazione del suo riferimento a Nothing non influisce sulla copia del chiamante.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow