Curso Básico de Programación
en Visual Basic

 

Entrega Treinta y siete: 14/Abr/2001
por Guillermo "guille" Som

Si quieres linkar con las otras entregas, desde el índice lo puedes hacer

 

Con la futura versión de Visual Basic, (la 7 o Visual Basic .Net), casi a la vuelta de la esquina, (se espera que para antes de que acabe este año 2001, esté en el mercado), es conveniente de que sepas de que va todo esto de las clases, no me refiero a "dar clases", (enseñar a alguien), sino a los módulos de clases, esos ficheros con la extensión .cls

De todas formas, creo que ya iba siendo hora de atacar el tema de las clases, entre otras cosas porque creo que es conveniente conocer cómo usarlas en nuestras aplicaciones, ya que pueden aliviarnos la tarea de crear y re-usar código; pero lo más importante es porque, si se usan de la forma adecuada, pueden permitirnos crear aplicaciones "troceadas" de forma que podamos mejorar partes de esos trozos sin necesidad de cambiar nada del resto del código que las usan... (no voy a adelantarme, así que, tendrás que esperar un poco para que realmente puedas entender de que estoy hablando).


Nota:

Como ya comenté en
la entrega 30, la versión, (o versiones), de Visual Basic que hace mejor uso de las clases, sobre todo de las colecciones, es la versión 5, (y mejor aún la 6, aunque con la versión 5 será más que suficiente), por tanto, si estás usando una versión anterior a la 5, puede que algunos de los ejemplos que muestre no funcione correctamente, sobre todo en el tema de las propiedades por defecto y en el manejo de colecciones propias. De todas formas, confío en que todo lo que explique te quede claro, incluso si usas la versión 4, ya que con versiones anteriores, no podrás usar los módulos de clases, simplemente porque ¡Visual Basic no sabe de que se trata!

Si no tienes el Visual Basic 5 o superior, puedes bajarte del sitio de Microsoft una versión reducida, que aunque no te permite crear ejecutables, si que puedes probar todo lo que aquí veamos, aunque sea en el propio entorno integrado (IDE).
Si no lo han cambiado de sitio, dicha versión de VB: la VB5CCE (Visual Basic 5 Control Creation Edition), la puedes encontrar en la siguiente dirección: http://msdn.microsoft.com/vbasic/downloads/tools/cce/default.aspx

Nota del 30/Jun/2003:
He actualizado el link del VB5CCE. Si vuelven a cambiarlo de sitio, es posible que en la dirección indicada no lo encuentres, así que tendrás que buscar en el sitio de Microsoft por "Control Creation Edition" y ¡suerte!
También te puedes pasar por la página VB-Resumen de mi sitio y allí seguramente el link esté más actualizado.


Ya sin más preámbulos, vamos a empezar con el tema que nos interesa:

Los módulos de clases

Seguramente habrás oído hablar sobre Programación Orientada a Objetos, (OOP Object Oriented Programing), Herencia, Polimorfismo, Encapsulación o simplemente que existen un tipo de fichero en Visual Basic, (a partir de la versión 4 inclusive), llamado módulos de clases, (esos fichero tienen la extensión .cls)

Nota:
Aunque no lo sepas, ya has estado trabajando con clases, ya que los formularios son realmente módulos de clases de un tipo especial.

Hasta ahora, todo el código que hemos estado utilizando, sobre todo los procedimientos, los hemos podido usar de forma directa, sin tener que hacer nada especial. Por ejemplo, en el código de la entrega anterior usábamos el procedimiento Buscar para realizar una búsqueda en la base de datos, dicho procedimiento estaba "escrito" en el propio formulario, por tanto la "visibilidad" o ámbito del mismo está limitado a dicho formulario.
El tema de la visibilidad ya se trató en
la entrega 26, en dicha entrega vimos un ejemplo de un formulario y un módulo BAS y cómo podíamos acceder a unas variables y a un procedimiento declarados con el "modificador" Public. También vimos cómo, podíamos acceder a variables con el mismo nombre declaradas en el formulario y el módulo BAS, simplemente indicando el nombre del módulo delante de la variable en cuestión, por tanto, te recomiendo que, si no tienes claro esto de la visibilidad de las variables y los procedimientos, le eches un vistazo a dicha entrega. Simplemente, a título recordatorio, te diré que la visibilidad es el ámbito que tienen las variables y los procedimientos, es decir en que parte del código son accesibles (visibles) y en cuales no.

Para usar los módulos de clases es muy importante tener estos conceptos claros, ya que de no ser así, vas a estar más perdido que una chiva en un garaje y no te enterarás de nada... te aviso.

 

Clases = Objetos (y viceversa)

Como te decía antes, sin saberlo, has estado usando objetos todo el tiempo. Un formulario es un tipo especial de clase, un botón, (CommandButton), es un objeto, (internamente es una clase). Todos los objetos tienen la característica de tener sus propios métodos, propiedades e incluso eventos.
Por ejemplo, el mencionado botón, tiene propiedades, como Caption, Visible, etc, También tiene eventos como Click, MouseMove, etc. Y también tiene métodos, por ejemplo Refresh, Move, etc. Lo mismo ocurre con los formularios, también tienen métodos, propiedades y eventos. Pero todos estos objetos ya existen, simplemente los usamos y ya está.
Lo que en esta entrega (y en otras más) aprenderemos es cómo poder crear nuestros propios objetos, con sus métodos, propiedades y eventos, y todo ello gracias a los módulos de clases.

Nota:
En el resto de esta entrega, usaré tanto la palabra objeto como la palabra clase, estas se referirán tanto a los objetos ya existentes, (más comúnmente conocidos como controles), como a los que nosotros podamos crear, ya sean tanto objetos de código como controles propiamente dicho, (en futuras entregas aprenderemos a crear nuestros propios controles ActiveX u OCX). Sólo espero que no te confundas.

 

Clases/Objetos = Encapsulación:

La ventaja de usar clases en nuestros proyectos es que podemos encapsular todo el código y tratarlas como si de cajas negras se tratasen, es decir, sabemos que es lo que hacen, cómo usarlas, pero no es imprescindible saber el código que se usa para que todo funcione.
Por tanto una de las características principales que un objeto (o clase) debe tener es que no dependa del exterior, es decir, que sea autosuficiente: todo la información que necesite se debe suministrar mediante las propiedades que dicha clase u objeto exponga al exterior, (mediante las propiedades públicas) y de igual manera, toda la información que un objeto deba mostrar al mundo exterior, se haga mediante las propiedades, métodos y eventos que dicha clase exponga al exterior, (declarados como públicos).

Bien, para entrar en calor, creo que lo mejor es ver un ejemplo.
Asi que abre el Visual Basic, crea un nuevo proyecto EXE y sigue estos pasos para añadir un módulo de clase:
Abre el menú Proyecto y pulsa en: Añadir Módulo de Clase, te mostrará un cuadro de diálogo mostrándote varias tipos de módulos de clase, pulsa en la primera que te muestra: Módulo de Clase, pulsa en Abrir y se añadirá un nuevo fichero, si te fijas en la ventana de propiedades, tendrá como nombre: Class1, (al menos así es como se llama en la versión inglesa de Visual Basic, que es la que yo uso).
Ese nombre será el que identificará al objeto cuando queramos acceder a él, (realmente será el que usaremos para crear variables que accedan a dicho objeto), por tanto es importante darle un nombre con el cual podamos, más o menos, identificar el uso para el que se ha creado dicho objeto.
Por tanto vamos a darle un nombre a la clase, y como en este ejemplo vamos a usar el objeto para almacenar los datos de un "colega" nuestro, le daremos el nombre: cColega.

Nota:
También puedes nombrarla Colega, la "c" es para indicar que se trata de una clase; no es una norma obligatoria, pero... es ampliamente usada por muchos programadores de Visual Basic, (entre ellos yo); otros usan el prefijo de la "c" sólo para el nombre del fichero, así que queda a tu criterio usarla en el nombre de la clase o no.

Esta clase tendrá varias propiedades, una para cada uno de los datos que manipulará, el Nombre, el e-mail y la fecha de nacimiento, además de un método que nos indicará que edad tiene dicho colega.
Veamos cómo añadir propiedades y métodos a una clase:

 

Añadir propiedades a una clase (u objeto):

Para crear las propiedades, podemos hacerlo de dos formas:

  1. La forma más simple, es declarando las propiedades como variables públicas: Cuando declaramos una variable Public en un módulo de clase, dicha variable se convierte en una propiedad.
  2. La otra forma es creando un procedimiento Property.

La ventaja de usar los procedimientos Property, es que podemos hacer comprobaciones extras que con las variables sería imposible hacer.
Por ejemplo, al asignar un valor a la propiedad e-mail, se podría comprobar que dicho valor contenga el signo arroba (@); en el caso la fecha de nacimiento, se podría validar que fuese una fecha correcta y en cualquiera de estos casos poder producir un error e incluso un evento para avisar de que algo no se ha hecho bien.
Al asignar un valor a una variable, dicho valor se asigna sin más, no se comprueba nada de nada, simplemente se asigna, sin embargo al usar un procedimiento para hacer dicha asignación podemos hacer las comprobaciones necesarias para asegurarnos que lo que se está asignando es lo correcto.

Cuando usamos procedimientos Property para almacenar valores de propiedades, nos vemos obligados a usar una variable que mantenga el valor asignado a dicha propiedad, esa variable se suele ocultar del mundo exterior declarándola Private, de esta forma esa variable sólo es visible dentro del código del módulo de clase. ¿Recuerdas lo que te dije del ámbito o visibilidad de las variables?

Veamos el código que se podría usar para implementar las propiedades de la clase cColega:

Para el nombre del colega, simplemente declaramos una variable pública:
Public Nombre As String

Para el e-mail crearemos una variable privada para guardar internamente la dirección de e-mail:
(como no podemos usar el signo - en el nombre de una variable, tendremos que usar email, sin signo de separación)
Private m_email As String
m_ es para indicar que es una variable declarada a nivel de módulo y por tanto visible en todo el módulo en el que se ha declarado.
Ahora tenemos que crear el procedimiento Property. Éste podemos crearlo de dos formas, usando la opción Añadir procedimiento... del menú de Herramientas o escribiendo el código directamente... vamos a usar el primer método:
Selecciona Añadir Procedimiento del menú Herramientas, te mostrará un cuadro de diálogo preguntando que tipo de procedimiento quieres crear y con que nombre. Selecciona la opción Property (Propiedad), en Ámbito (Scope) selecciona Public y escribe email en la caja de texto.
Se crearán dos nuevos procedimientos:

Public Property Get email() As Variant

End Property

Public Property Let email(ByVal vNewValue As Variant)

End Property

Cada uno de estos dos procedimientos son: Property Get para devolver el valor de la propiedad y Property Let para asignar un nuevo valor a la propiedad.

Nota:
Por defecto el tipo de datos asignado a un procedimiento Property es del tipo Variant, por tanto habrá que modificarlo para que sea del tipo que vamos a usar.

En el procedimiento que se usa para devolver el contenido actual de la propiedad, simplemente devolvemos el contenido de la variable privada:


Public Property Get email() As String
    ' Devolver el contenido de esta propiedad,
    ' el cual está almacenado en la variable privada
    email = m_email
End Property

En el procedimiento que se usa para asignar un nuevo valor a la propiedad, es donde tenemos que hacer la comprobación de que se asigna un valor que representa una dirección de correo electrónico, (realmente sólo se comprueba que tenga el signo @)


Public Property Let email(ByVal NewValue As String)
    ' Comprobar que el nuevo valor a asignar tenga el signo @
    If InStr(NewValue, "@") = 0 Then
        ' Asignamos los datos del error, pero no lo producimos
        With Err
            .Number = 13
            .Description = "El valor asignado a email no es una dirección de correo electrónico."
            .Source = "cColega.email = " & NewValue
            ' Para producir el error, usariamos
            '.Raise 13
        End With
    Else
        ' Asignamos el nuevo valor a la variable privada
        m_email = NewValue
    End If
End Property

En este procedimiento se comprueba que la cadena tenga el signo arroba, si no es así, la comparación realizada se cumple y se asigna al objeto Err un error indicando lo que se ha hecho mal; si quitas el comentario que hay delante de .Raise, el error se produce en la clase y de esa forma no se detectaría en la aplicación que use la clase.
En caso de que la cadena asignada a la propiedad tenga el signo @, el nuevo valor se asignará a la variable privada.

Dentro de un poco veremos el código usado para asignar los valores a las propiedades y manejar los errores que se produzcan.

Antes vamos a codificar la propiedad FechaNacimiento.
Como esta propiedad contendrá una fecha, lo lógico sería que el tipo de datos fuese Date, el problema o la ventaja, según se mire, es que si asignamos un valor erróneo a dicha propiedad, será el propio Visual Basic el que se encargue de avisarnos de que no es correcta. Pero si queremos detectar el valor erróneo dentro del procedimiento o bien usar algún tipo de conversión "inteligente" de una fecha que supuestamente es errónea, (por ejemplo: permitir introducir la fecha con distintos signos de separación y asignarlo en el formato que internamente queramos), entonces lo mejor sería usar otro tipo de datos, por ejemplo del tipo String; como siempre, quedará a tu criterio el usar un tipo de datos u otro.

Sea como fuere, necesitaremos una variable privada para mantener el valor asignado, en este caso he declarado una variable de esta forma:
Dim m_FechaNacimiento As Date
El declarar una variable de tipo Date no implica, ni obliga a, que tengamos que usar ese tipo de datos en los procedimientos de la propiedad, lo único obligatorio es que tanto el valor devuelto por el procedimiento Property Get sea del mismo tipo de datos que el de la variable usada en el procedimiento Property Let.
Es decir, no podemos tener un procedimiento Property devolviendo un tipo de datos y recibiendo otro diferente, independientemente del tipo de datos que tenga la variable privada usada para contener el valor.

La forma más fácil de "codificar" ese procedimiento sería de esta forma:

Public Property Let FechaNacimiento(ByVal NewValue As String)
    ' Si no es una fecha correcta...
    If IsDate(NewValue) = False Then
        ' Asignar un error
        With Err
            .Number = 13
            .Description = "El valor asignado a FechaNacimiento no es una fecha válida."
            .Source = "cColega.FechaNacimiento = " & NewValue
        End With
    Else
        m_FechaNacimiento = NewValue
    End If
End Property

Esta otra es algo más complicada, en ella se permite aceptar fechas en diferentes formatos así como también en formatos sin separadores, estos son algunos de los "valores" aceptados para la fecha 13/04/2001: 130401, 13042001, 134, 1304, 13/4, 13/4/01, 13/4/2001, 13.4.01, 13.04.01, 13-04-01, 13-4, etc. e incluso valores como 0413, 04.13.01 (en este caso porque no hay duda de que 13 no puede ser un mes...).


Public Property Let FechaNacimiento(ByVal NewValue As String)
    Dim i As Long
    Dim s As String
    '
    ' Comprobar si se usan puntos como separador
    ' si es así, cambiarlos por /
    Do
        i = InStr(NewValue, ".")
        If i Then
            Mid$(NewValue, i, 1) = "/"
        End If
    Loop While i
    '
    ' Comprobar si se usan - como separador
    ' si es así, cambiarlos por /
    Do
        i = InStr(NewValue, "-")
        If i Then
            Mid$(NewValue, i, 1) = "/"
        End If
    Loop While i
    '
    s = ""
    Do
        i = InStr(NewValue, "/")
        If i Then
            s = s & Right$("0" & Left$(NewValue, i - 1), 2) & "/"
            NewValue = Mid$(NewValue, i + 1)
        End If
    Loop While i
    NewValue = s & NewValue
    '
    If InStr(NewValue, "/") Then
        If Len(NewValue) = 5 Then
            ' Si es igual a 5 caracteres, es que falta el año
            NewValue = NewValue & "/"
        ElseIf Len(NewValue) < 3 Then
            ' Si es menor de 3 caracteres es que falta el mes
            NewValue = NewValue & "/" & CStr(Month(Now)) & "/"
        End If
    ElseIf Len(NewValue) < 3 Then
        NewValue = NewValue & "/" & CStr(Month(Now)) & "/"
    Else
        s = ""
        For i = 1 To 2
            s = s & "/" & Mid$(NewValue, (i - 1) * 2 + 1, 2)
        Next
        s = s & "/" & Mid$(NewValue, 5)
        NewValue = s
    End If
    NewValue = Trim$(NewValue)
    '
    ' Comprobar si tiene una barra al principio, si es así, quitarla
    If Left$(NewValue, 1) = "/" Then
        NewValue = Mid$(NewValue, 2)
    End If
    ' Si tiene una barra al final, es que falta el año
    If Right$(NewValue, 1) = "/" Then
        NewValue = NewValue & CStr(Year(Now))
    End If
    '
    ' Convertir la fecha, por si no se especifican todos los caracteres
    ' Nota: Aquí puedes usar el formato que más te apetezca
    NewValue = Format$(NewValue, "dd/mm/yyyy")
    '
    ' Si no es una fecha correcta...
    If IsDate(NewValue) = False Then
        ' Asignar un error
        With Err
            .Number = 13
            .Description = "El valor asignado a FechaNacimiento no es una fecha válida."
            .Source = "cColega.FechaNacimiento = " & NewValue
        End With
    Else
        m_FechaNacimiento = NewValue
    End If
End Property

Bien, todo esto que hemos visto hasta ahora es aplicable a cualquier tipo módulo, no solo a los de clases, (por ejemplo, en ciertas ocasiones es bastante útil usar procedimientos Property en los formularios).
Ahora vamos a ver cómo podemos usar esta clase en un proyecto y poder comprobar cómo se detectan los posibles errores si la asignación no es correcta.

Para probar, vamos a usar el formulario que tenemos en el proyecto que hemos creado y en el cual debe estar la clase que acabamos de ver.

Nota:
Deja tu mente en blanco, y presta atención a lo que sigue... si aún asi no consigues entenderlo... apúntate a algún curso de meditación trascendental y después sigue leyendo...

 

Cómo usar las clases, (objetos), en un proyecto.
(
Ahora viene el meollo de la cuestión)

Si los procedimientos que acabamos de crear estuviesen en un módulo BAS, los usaríamos sin más, pero, como dichos procedimientos están "incluidos" en un módulo de clase, o en un objeto, (según prefieras llamarlo), antes tenemos que tener una variable que sea del mismo tipo que dicho objeto y además crearlo. Ya que los procedimientos, (Sub, Function o Property), declarados en un módulo de clase sólo existen, o son accesibles o son visibles, porque pertenecen a la clase (u objeto). Es decir, no es suficiente tener una variable del tipo especificado, sino que le tenemos que indicar al Visual Basic que lo cree... que le de vida... (¿complicado? un poco, no todo va a ser coser y cantar... ¿que te crees?)

Todos los procedimientos e incluso las variables públicas declaradas en un módulo de clase "pertenecen" a dicha clase y se convierten en propiedades y métodos de dicha clase y por tanto sólo son accesibles gracias a que dicha clase "existe".

Veamos cómo crear una variable del tipo cColega:
Dim oColega As cColega
Esto simplemente le indica al Visual Basic que la variable oColega es del tipo de cColega, pero nada más.
Si intentáramos usarla, nos daría un error diciendo que: "la variable de objeto no está establecida"
Y es cierto... ya que, lo único que hemos hecho es indicar que oColega puede "apuntar" a un objeto del tipo cColega, pero no le hemos dicho "qué" objeto es.

Para clarificar un poco las cosas: (si es que todo esto se puede clarificar...)
Cuando añadimos un control a un formulario, Visual Basic crea una variable, (con el nombre que le hemos dado a dicho control), y de forma automática, (sin que nos enteremos), crea un nuevo objeto que lo asigna a dicha variable; sin embargo con las clases, somos nosotros los que tenemos que decirle que cree un nuevo objeto y lo asigne a dicha variable.

Por tanto, después de declarar una variable para que contenga el objeto, hay que crear un objeto nuevo y asignarlo a dicha variable, todo esto se hace de la siguiente forma:
(aunque no siempre tiene porqué ser así...
¡Guille! no compliques más la cosa, que bastante complicada está ya...),
Set oColega = New cColega
Esto le indica al VB que cree un nuevo objeto del tipo cColega: New cColega y dicho objeto lo asigne a la variable oColega: Set oColega =

Nota:
Para asignar un objeto a una variable, se usa SET, si no usamos Set, lo que estaríamos asignando a dicha variable sería el contenido de la propiedad por defecto.

Por ejemplo, para guardar en una variable el control Text1, haríamos esto:
Dim unTextBox As TextBox
Set unTextBox = Text1

Pero para guardar en una variable el contenido de la propiedad por defecto de Text1, (es decir el contenido de la propiedad Text), haríamos esto otro:
Dim sUnTexto As String
sUnTexto = Text1
Que es lo mismo que hacer: sUnTexto = Text1.Text


Seguramente dirás... ¿tan complicado es de entender? Si está "chupao"
Me gustaría creer que no te ha parecido tan complicado y que lo has entendido al 100%, pero... ¡No te preocupes! ¡Te aseguro que acabarás entendiéndolo! ya que si no lo haces, te quedarás estancado con el Visual Basic 6 y no podrás disfrutar de lo que la nueva versión ofrece... je, je, je... así que... si no lo has entendido... ponte las pilas y aplícate al máximo.

Veamos ahora el código del formulario con el código para usar la clase.
Este formulario tiene tres etiquetas, tres cajas de texto: Text1(0), Text1(1) y Text1(2) un botón llamado cmdAsignar y un ListBox.


Private Sub cmdAsignar_Click()
    ' Dimensionamos una variable del tipo del objeto (clase)
    Dim oColega As cColega
    ' Creamos un nuevo objeto y lo asignamos a la variable
    Set oColega = New cColega
    '
    ' Detectamos los errores que se produzcan
    On Error Resume Next
    '
    ' Asignamos a la propiedad Nombre el contenido de Text1(0)
    oColega.Nombre = Text1(0).Text
    ' Asignamos a la propiedad email el contenido de Text1(1)
    oColega.email = Text1(1).Text
    ' Si se produce un error
    If Err Then
        ' Mostrar el mensaje de aviso
        MsgBox "Se ha producido un error en: " & Err.Source & vbCrLf & Err.Description
        ' Posicionar el cursor en la caja de textos del email
        Text1(1).SetFocus
        ' Limpiamos el error
        Err = 0
        ' Salimos de este procedimiento
        Exit Sub
    End If
    '
    ' Asignamos la fecha de nacimiento
    oColega.FechaNacimiento = Text1(2).Text
    If Err Then
        ' Mostrar el mensaje de aviso
        MsgBox Err.Number & ", se ha producido un error en: " & Err.Source & vbCrLf & Err.Description
        ' Posicionar el cursor en la caja de textos del email
        Text1(2).SetFocus
        ' Limpiamos el error
        Err = 0
        ' Salimos de este procedimiento
        Exit Sub
    End If
    ' ... resto del código
    List1.AddItem oColega.Nombre & " - " & oColega.FechaNacimiento
End Sub

Otras formas de declarar una clase.

Para terminar esta entrega, (y dejarte que recapacites sobre la "existencialidad" de los objetos), vamos a ver otra forma de "crear" un objeto al mismo tiempo que lo declaramos:
Dim oColega As New cColega
Esto lo explico, entre otras cosas, para que sepas que se puede hacer, ya que seguramente lo habrás visto en los ejemplos que acompañan al Visual Basic así como en algunos listados que hayan podido caer en tus manos, incluso (mea culpa) en listados que yo mismo he escrito, aunque prometo que NUNCA más lo volveré a hacer... e incluso te pediría que me dijeras en que listados, (siempre que sean míos), has visto esa forma de declarar las clases, para que pueda corregirlo...

Te recomiendo y te rogaría, (y si pudiera, te obligaría), encarecidamente de que NUNCA crees clases de esta forma.

¿Por qué no es recomendable crear las clases de esta forma, si es más fácil e incluso más lógico?
Porque crear una clase usando New al mismo tiempo que se declara un objeto añade más código, (aunque nosotros no lo veamos), además de que no tenemos el control total sobre cuando se crea el objeto, ya que el objeto se crea cuando intentamos usarlo, no cuando "explícitamente" le indicamos que lo cree...
¿Por qué añade más código?
Porque Visual Basic nunca sabe si el objeto está creado o no, así que, junto a cada acceso al objeto añade una comprobación para saber si dicho objeto está creado o no, ya que si no está creado, "tiene" que crear uno nuevo para que podamos usarlo.

 

¿Cuando se destruye un objeto?
(es decir: ¿cuando deja de existir un objeto?)

Un objeto, (o una variable que apunte o haga referencia a un objeto), tiene la misma duración o ámbito que cualquier variable.
Por ejemplo: un objeto declarado dentro de un procedimiento existe mientras dure dicho procedimiento, cuando el procedimiento termina, el objeto "desaparece", se esfuma, deja de existir.

 

¿Cómo podemos destruir explícitamente un objeto?
(es decir: ¿cuando podemos hacer que un objeto desaparezca porque nosotros queremos que desaparezca?)

Si queremos que una variable que apunta a un objeto deje de hacer referencia a ese objeto, tendremos que asignarle el valor Nothing a dicha variable, (usando Set):
Set oColega = Nothing.

No es por complicarte la vida, pero no siempre todo esto es cierto... ya que el Visual Basic se encarga de saber cuando se puede "liberar" un objeto, para ello lleva su propia "cuenta" de cuando un objeto debe estar "vivo" o cuando debe acabar con su existencia... todo esto es aplicable también a los formularios, ya que, como dije al principio, los formularios también son también clases u objetos, con un tratamiento diferente, pero clases al fin y al cabo.

Para muestra un botón.
Crea un nuevo proyecto Exe, añade un segundo formulario, (el Form1 se crea junto con el proyecto), añade una etiqueta al segundo formulario (Form2) que ocupe el ancho del mismo. Añade este código al Form2:


'------------------------------------------------------------------------------
' Segundo formulario para "probar" que no todo es lo que parece     (14/Abr/01)
'
' ©Guillermo 'guille' Som, 2001
'------------------------------------------------------------------------------
Option Explicit

' Valor interno para la propiedad del Form2
Private m_FechaCreacion As Date

Private Sub Form_Load()
    ' Asignar el valor de la fecha de creación
    ' (aunque realmente es la fecha en que se carga el formulario)
    m_FechaCreacion = Now
    '
    Label1 = "Form creado el: " & m_FechaCreacion
End Sub

Public Property Get FechaCreacion() As Date
    ' Propiedad de sólo lectura (no existe el procedimiento Property Let)
    FechaCreacion = m_FechaCreacion
End Property

En el primer formulario añade dos etiquetas (Label1 y Label2) y dos botones (Command1 y Command2) y añade este código:

'------------------------------------------------------------------------------
' Formulario para "probar" que no todo es lo que parece             (14/Abr/01)
'
' ©Guillermo 'guille' Som, 2001
'------------------------------------------------------------------------------
Option Explicit

Private Sub Command1_Click()
    ' Mostramos el Form2
    Form2.Show
    ' Mostrar la fecha de creación
    Label1 = Form2.FechaCreacion
End Sub

Private Sub Command2_Click()
    ' Descargar el formulario
    Unload Form2
    ' Si no se asigna Nothing al Form2,
    ' la fecha mostrada será la misma que antes... es decir, aún existe el objeto
    'Set Form2 = Nothing
    '
    ' Una vez descargado, accedemos a la propiedad de la fecha de creación:
    Label2 = "El Form2 se creó el: " & Form2.FechaCreacion
End Sub

Ejecuta el proyecto y pulsa en el Command1 para que se muestre el Form2.
Pulsa en el Command2 para descargar el Form2.
Fíjate que la fecha mostrada es la misma que cuando se creó dicho formulario.
Esto prueba que el formulario, a pesar de estar descargado, aún existe, ya que conserva el valor de la variable interna y si esa variable existe es que el formulario aún está en algún lugar de la memoria.

Ahora quita el comentario que hay delante de Set Form2 = Nothing y vuelve a ejecutar el proyecto.
Pulsa primero en Command1 y después en Command2 (igual que antes)
Fíjate que la fecha mostrada al cerrar el formulario es otra, (realmente es 00:00:00)
Esto, aunque parezca que prueba algo... realmente no prueba nada, salvo que la fecha, (guardada en la variable privada), no está asignada.
Y no está asignada, porque al asignar Nothing al "objeto" Form2, éste se destruye y con él se destruye la variable.
Pero al acceder de nuevo a la propiedad, el Form2 se vuelve a crear ya que, Visual Basic usa una variable, (llamada Form2), como si estuviese declarada con New, ( es decir VB hace algo parecido a esto: Dim Form2 As New Form2), por tanto, cada vez que usamos dicha variable, si el objeto no existe, Visual Basic vuelve a crearlo, pero el valor de la propiedad no se asigna, ya que la asignación se hace en el evento Load y ese evento sólo se produce al cargarse el formulario de forma explícita con Load o al mostrarlo con Show.

El evento que siempre se ejecuta al crearse un objeto (o clase), es Initialize, en el caso de los formularios es: Form_Initialize.
Por tanto, si en ese evento se asigna el valor de la fecha de creación a la variable m_FechaCreacion...

Private Sub Form_Initialize()
    m_FechaCreacion = Now
End Sub

¿Que valor se mostrará en Label2 después de asignar Nothing a Form2?
1.- La misma que había al mostrar el formulario, (la que se muestra en Label1).
2.- El valor 00:00:00
3.- Un valor posterior al mostrado en Label1.

Solución: 3.- Un valor posterior al mostrado en Label1, ya que al acceder a una propiedad de una clase que no existe, es decir, que aún no está creada, dicha clase se crea y por tanto se produce el evento Initialize de la clase y es en este evento cuando se asigna el valor de la fecha actual. Si quieres, quita la asignación que hay en el evento Form_Load del Form2 y verás que aún así, el valor es diferente.

¿Te das cuenta del "peligro" de usar New al declarar un objeto?
¿Por qué lo hace Visual Basic con los formularios a pesar de saber que es peligroso?
Seguramente para facilitarnos las cosas, pero... (por suerte, esto cambia en la nueva versión de Visual Basic: VB.NET)

Para terminar, (seguramente esto hará que lo entiendas mejor... en eso confío...), vamos a ver que es lo que ocurre si nos "saltamos" la auto creación que hace Visual Basic de la variable que contiene la clase Form2, (recuerda que internamente y sin que nos enteremos, declara una variable de esta forma: Dim Form2 As New Form2)

¿Cómo nos saltamos esta auto creación?
Creando nosotros mismos una variable que apunte a la clase Form2, tal y como haríamos con cualquier otra clase.
Por tanto modifica el código del Form1 para que sea como el que te muestro: (el código del Form2 queda igual)

'------------------------------------------------------------------------------
' Formulario para "probar" que no todo es lo que parece             (14/Abr/01)
'
' ©Guillermo 'guille' Som, 2001
'------------------------------------------------------------------------------
Option Explicit
'
' Variable del tipo Form2
Private elForm2 As Form2

Private Sub Form_Load()
    ' Creamos el objeto
    Set elForm2 = New Form2
End Sub

Private Sub Command1_Click()
    ' Mostramos el Form2
    elForm2.Show
    ' Mostrar la fecha de creación
    Label1 = elForm2.FechaCreacion
End Sub

Private Sub Command2_Click()
    ' Descargar el formulario
    Unload elForm2
    ' Destruimos el objeto (lo eliminamos de la memoria)
    Set elForm2 = Nothing
    '
    ' Si una vez descargado, accedemos a la propiedad de la fecha de creación:
    ' ESTO PRODUCIRA UN ERROR, YA QUE ¡LA VARIABLE elForm2 NO EXISTE!
    Label2 = "El Form2 se creó el: " & elForm2.FechaCreacion
End Sub

Ejecuta el proyecto y haz la misma prueba que antes, primero pulsa en Command1 y después en Command2.
En esta ocasión se mostrará un error de que la variable no está establecida.
¿Lo comprendes?
Cuando asignamos Nothing a una variable que apunta a un objeto, dicha variable se libera de cualquier referencia, es decir, se destruye el objeto al que apuntaba y por tanto ya no está asignada.

Aunque todo este rollo te lo he mostrado con formularios en lugar de clases creadas por nosotros, es por dos motivos:
El primero es para que lo puedas entender mejor, ¡espero!
El segundo es para que te des cuenta que un formulario realmente es una clase.

La moraleja de todo esto es para que "desistas" en usar variables de clases dimensionadas con New.

Segunda moraleja: si usas formularios con variables creadas por ti mismo, NO USES NUNCA LA VARIABLE CREADA POR VISUAL BASIC, ya que si lo haces, todo lo aprendido se va al garete... y si no, prueba esta línea, ponla justo después de la asignación a Nothing que hacemos a la variable elForm2 en el evento Command2_Click:
Label2 = "El Form2 se creó el: " & Form2.FechaCreacion
Al usar la variable creada por Visual Basic, no se producirá ningún error, pero el objeto sigue estando en el limbo del VB...

Je, je, je... que me gusta liar las cosas...

Y hasta aquí hemos llegado por hoy... (mucho follón, ¿verdad?)
No te preocupes si aún no tienes las cosas muy claras, ya que en las próximas entregas seguiremos trabajando con clases.
Y ya sabes que la mejor forma de aprender es practicando, así que, practicaremos y practicaremos hasta que aprendas.

Nos vemos
Guillermo


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille