|
Curso básico
de programación en Visual Basic
Lección
37
Las
clases
|
Realizamos
programas para la gestión de empresas. Empresas medianas y
pequeñas. Programas de contabilidad, cartera de pedidos
clientes proveedores, facturación control de albaranes,
tesorería cartera de cobros y pagos y estadísticas.
Nuestro
agradecimiento a todos los que por unas causas o por otras
visitan nuestra web. Gestión de empresas PYMES. Curso de
programación de Visual Basic.
|
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:
- 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.
- 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
'
' ©Guillermo 'guille' Som, 2003
'---------------------------------------------------------------------------
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
'
' ©Guillermo 'guille' Som, 2003
'---------------------------------------------------------------------------
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
'
' ©Guillermo 'guille' Som, 2003
'---------------------------------------------------------------------------
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

|