Buscar..


Introducción

Los modificadores ByRef y ByVal son parte de la firma de un procedimiento e indican cómo se pasa un argumento a un procedimiento. En VBA, se pasa un parámetro ByRef menos que se especifique lo contrario (es decir, ByRef está implícito si está ausente).

Nota En muchos otros lenguajes de programación (incluido VB.NET), los parámetros se pasan implícitamente por valor si no se especifica ningún modificador: considere la posibilidad de especificar los modificadores ByRef explícitamente para evitar posibles confusiones.

Observaciones

Pasando matrices

Las matrices deben ser pasadas por referencia. Este código se compila, pero genera un error de tiempo de ejecución 424 "Objeto requerido":

Public Sub Test()
    DoSomething Array(1, 2, 3)
End Sub

Private Sub DoSomething(ByVal foo As Variant)
    foo.Add 42
End Sub

Este código no se compila:

Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
    foo.Add 42
End Sub

Pasando variables simples ByRef y ByVal

Pasar ByRef o ByVal indica si el valor real de un argumento se pasa al CalledProcedure mediante el CallingProcedure , o si una referencia (llamada puntero en otros idiomas) se pasa al CalledProcedure .

Si se pasa un argumento ByRef , la dirección de memoria del argumento se pasa al CalledProcedure y cualquier modificación a ese parámetro por el CalledProcedure se hace al valor en el CallingProcedure .

Si se pasa un argumento ByVal , el valor real, no una referencia a la variable, se pasa a CalledProcedure .

Un ejemplo simple ilustrará esto claramente:

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

Otro ejemplo:

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


Modificador por defecto

Si no se especifica ningún modificador para un parámetro, ese parámetro se pasa implícitamente por referencia.

Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub

El parámetro foo se pasa ByRef tanto en DoSomething1 como en DoSomething2 .

¡Cuidado! Si viene a VBA con experiencia de otros idiomas, es muy probable que este sea el comportamiento opuesto al que está acostumbrado. En muchos otros lenguajes de programación (incluido VB.NET), el modificador implícito / predeterminado pasa los parámetros por valor.


Pasando por referencia

  • Cuando se pasa un valor ByRef , el procedimiento recibe una referencia al valor.

    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
    

    Llamar a las salidas del procedimiento de Test 84. DoSomething se da foo y recibe una referencia al valor y, por lo tanto, funciona con la misma dirección de memoria que la persona que llama.

  • Cuando se pasa una referencia ByRef , el procedimiento recibe una referencia al puntero.

    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
    

    El código anterior genera el error de tiempo de ejecución 91 , porque el autor de la llamada está llamando al miembro Count de un objeto que ya no existe, porque DoSomething recibió una referencia al puntero del objeto y lo asignó a Nothing antes de regresar.


Forzar ByVal en el sitio de la llamada

Al usar paréntesis en el sitio de la llamada, puede anular ByRef y forzar la aprobación de un argumento 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

El código anterior genera 42, independientemente de si ByRef se especifica implícita o explícitamente.

¡Cuidado! Debido a esto, el uso de paréntesis extraños en las llamadas a procedimientos puede introducir errores fácilmente. Preste atención al espacio en blanco entre el nombre del procedimiento y la lista de argumentos:

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

Pasando por valor

  • Cuando se pasa un valor ByVal , el procedimiento recibe una copia del valor.

    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
    

    Llamando a las salidas del procedimiento de Test 42. DoSomething se da foo y recibe una copia del valor. La copia se multiplica por 2 y luego se desecha cuando finaliza el procedimiento; La copia de la persona que llama nunca fue alterada.

  • Cuando se pasa una referencia ByVal , el procedimiento recibe una copia del puntero.

    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
    

    Llamando a la anterior Test salidas de procedimiento 1. DoSomething es dada foo y recibe una copia del puntero a la Collection de objetos. Debido a que la variable de objeto foo en el alcance de Test apunta al mismo objeto, agregar un elemento en DoSomething agrega el elemento al mismo objeto. Debido a que es una copia del puntero, establecer su referencia en Nothing no afecta a la propia copia de la persona que llama.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow