Pasa Programación, apuntes y soluciones con Access
Página principal Mis apuntes de Access Mis utilidades Mis ejemplos Mis agradecimientos Enlaces a otras Webs

Usando variables en VBA

  1. Índice.
  2. Variables explícitas e implícitas.
  3. Definir los tipos de las variables.
  4. Ámbito y Accesibilidad de las variables.
  5. Parámetros por valor y por referencia.
  6. Variables de objeto.
  7. Bibliografía.

Variables de objeto

Hasta ahora hemos visto como dimensionar y declarar variables de tipos simples (texto, números y fecha), pero, ¿qué debemos hacer si deseamos declarar variables que contengan objetos? Pues prácticamente lo mismo: declaramos la variable para reservar un espacio en memoria que la contenga, y ya podemos acceder al objeto a través de esa variable. Lo que hay que tener en cuenta es que, además de declarar la variable para inicializarla, debemos inicializar el objeto para poder trabajar con él:

Usar una variable que haga referencia a un objeto
Sub DeclararObjeto()
    Dim l As Long

    Primero declaramos la variable
    Dim obj As Collection

    Después inicializamos el objeto
    Set obj = New Collection

    Y ya podemos trabajar con la variable
    obj.Add "Primer elemento", "uno"
    obj.Add "Segundo elemento", "dos"
    obj.Add "Tercer elemento", "tres"

    For l = 1 To obj.Count
        Debug.Print "Elemento " & l & " en la colección: " & obj(l)
    Next

    Do While obj.Count > 0
        obj.Remove 1
    Loop
End Sub

En el ejemplo, hemos dimensionado la variable obj para que almacene un objeto de tipo Collection. Luego hemos inicializado el objeto gracias a la cláusula New, y ya hemos podido trabajar con él: agregar elementos, mostrar los elementos guardados en la colección y eliminarlos de la colección.

En VBA, las 2 líneas para declarar la variable e inicializar el objeto, las podemos unir en una sola:

Dim obj As New Collection

Pero este método no hace exactamente lo mismo: según Microsoft, utilizar New con la instrucción Set es más rápido y es el método que se recomienda.

Destruir los objetos

Las variables de tipos simples no las destruíamos, ni indicábamos que fueran eliminadas de la memoria, pero con las variables de objetos sí que lo podemos hacer. Si indicamos al final del procedimiento anterior:

Set obj = Nothing

Estamos indicando que se elimine la variable y el objeto al que apunta. Pero, ¿es realmente necesario? La respuesta, como muchas cosas en esta vida, depende del contexto. En nuestro ejemplo, creamos una variable local. Y al ser una variable local, ésta será creada y destruida cada vez que usemos o ejecutemos el procedimiento, con lo que no necesitamos destruir la variable: VBA se encargará de destruirla cuando salga del procedimiento. Pero no ocurre lo mismo con las variables públicas o privadas de un módulo estándar o módulo de clase. En estos casos, sí que debemos destruirlas, o permanecerán cargadas, como mínimo, mientras tengamos abierta la base de datos y, en según que casos, hasta después de haber cerrado la base de datos. Esto conlleva una pérdida de memoria y de rendimiento, que a l@s usuari@s de nuestra base de datos nos les gustará lo más mínimo.

Mucho más importante que pensar en destruir los objetos es el cerrarlos, que aunque parezca lo mismo, no lo es. Por ejemplo, podemos tener dos objetos de la librería de DAO: Database y Recordset, con los que accedemos a otra base de datos, abrimos un conjunto de registros y los actualizamos con los datos que le hemos pasado a nuestra función:

Sub ActualizarTablaExterna(valor1 As String, valor2 As String, valor3 As Integer)
    'Dimensionamos los objetos
    Dim dbs As DAO.Database
    Dim rst As DAO.Recordset

    'A estos objetos no los inicializamos con New,
    'se crean a partir de métodos de otros objetos

    Set dbs = Workspaces(0).OpenDatabase("P:\Test\test.mdb")
    Set rst = dbs.OpenRecordset("SELECT campo1, campo2, campo3 FROM TablaPruebas")

    'Agregamos un registro
    With rst
        .AddNew
        .Fields("campo1") = valor1
        .Fields("campo2") = valor2
        .Fields("campo3") = valor3
        .Update
    End With

    'Ahora hacemos lo más importante, cerrar:
    rst.Close
    dbs.Close
End Sub

Al cerrar los objetos con sus métodos Close, nos aseguramos de cerrar las conexiones a la tabla y a la base de datos. De esta forma, no saturamos la base de datos con multitud de conexiones sin usar, liberamos memoria, y la próxima vez que accedamos a la base de datos con la misma función, no tendremos extraños problemas de lentitud en la ejecución del procedimiento. O, al cerrar los objetos, podemos volver a abrirlos apuntando a otra base de datos u otra tabla, sin necesidad de declarar otras variables del mismo tipo.

Las ventajas de cerrar los objetos son muchas, pero hay objetos que no se pueden cerrar. ¿Por qué? Porque no disponen de un método para cerrarlos. Este tipo de objetos suelen ser más sencillos, o no acceden a otros objetos o archivos de la base de datos, con lo que al destruirlos, se elimina el objeto y todas sus referencias.

Los objetos son tipos por referencia

Otra cuestión sobre las variables de objeto, es que no mantienen sus datos internamente, al igual que ocurría con las variables de tipos simples. Si probamos el siguiente código:

Sub ObjetosPorRef()
    'Dimensionamos los tipos simples
    Dim int1 As Integer
    Dim int2 As Integer

    'Damos valor a las variables
    int1 = 10

    'A la segunda variable, le damos el
    'valor de la primera

    int2 = int1

    'Y la modificamos
    int2 = 60

    'Y la primera variable no ha cambiado
    Debug.Print "int1 tiene el valor " & int1

    'Dimensionamos los objetos
    Dim clase1 As ClaseObjetos
    Dim clase2 As ClaseObjetos

    'Y los inicializamos
    Set clase1 = New ClaseObjetos

    'Damos un valor al primer objeto
    clase1.Dato = "Pepe"

    'Al segundo objeto, le asignamos
    'lo que tiene el primero

    Set clase2 = clase1

    'Y modificamos el segundo objeto
    clase2.Dato = "Juan"

    'Pero el primero también ha sido modificado
    Debug.Print "Clase1 tiene el valor " & clase1.Dato
End Sub

Suponiendo que hemos creado un módulo de clase de nombre ClaseObjetos en la misma base de datos con el siguiente código:

Option Compare Database
Option Explicit

Public
Dato As String

Veremos que la variable int1 mantiene su valor inicial, 10, mientras que la variable clase1 no: de tener el valor Pepe ha pasado ha tener el valor Juan, aunque a la variable a la que se le ha cambiado el valor ha sido a clase2.

Esto ocurre porque las variables de tipos simples, como Integer, almacenan su valor y cuando se asignan a otras variables, les asignamos el valor que tienen guardado. Es el mismo caso que cuando pasamos los parámetros de un procedimiento con la cláusula ByVal.

En cambio, las variables de objeto almacenan una dirección de memoria, donde se encuentran las propiedades, funciones y métodos de ese objeto que se ha creado. Y cuando asignamos a una variable de clase otra variable de clase u objeto, le estamos dando la dirección de memoria que tiene guardada la segunda variable, con lo que las 2 variables acceden directamente al mismo objeto físico. Es decir, funciona de la misma forma que si a un parámetro de una función le asignamos el valor ByRef.

Hay objetos que permiten asignarlos mediante métodos, sin que apunten distintas variables a la misma posición de memoria. Por ejemplo, el objeto Recordset de DAO dispone del método Clone, que devuelve una copia del objeto Recordset, pero sin que apunte a la misma dirección de memoria que el Recordset original. Así, aunque modifiquemos la copia, no estaremos modificando el original.

2008 Patxi Sanz
Libro de visitas
Hosted by www.Geocities.ws

1