|
Curso básico
de programación en Visual Basic
Lección
42
Crear
colecciones personalizadas
|
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.
|
Buenas, ya estoy de
nuevo por aquí... sí, se que han pasado un montón de días...
pero...
En esta entrega
vamos a seguir con las clases, en particular con las colecciones
personalizadas.
Si quieres refrescarte la memoria, podrías volver a las entregas
anteriores sobre las clases, según recuerdo son las entregas 37,
38 y 39.
En la entrega 38 se
trató el tema de las colecciones, pero de forma genérica, en esta
entrega veremos cómo crear nuestra propia clase-colección para que
maneje elementos del tipo que nosotros queramos.
Es habitual que los
nombres de las colecciones que contendrán elementos de un tipo
definido por nosotros, (en este caso de una clase definida por
nosotros, no un tipo de datos creado mediante TYPE), tengan un
nombre que sea el plural del nombre del objeto que contendrá. Por
ejemplo, si la colección va a contener elementos del tipo cColega,
se llamaría cColegas. Esto simplemente es una
"recomendación", ya que el nombre que tenga la colección
es el que tú decidas, al Visual Basic le da igual que se llame
cColegas o LosColegas o el nombre que se te venga a la cabeza...
siempre y cuando sea un nombre "válido".
Para seguir con la clase definida en la entrega 39, vamos a llamarla
cColegas y contendrá elementos del tipo cColega.
Esta colección
tendrá los métodos habituales en todas las colecciones,
recordémoslos:
Add, para añadir nuevos elementos.
Remove, para eliminar un elemento en cuestión.
Count, para saber cuantos elementos contiene.
Item, para acceder a dicho elemento.
Además se podrá recorrer la colección usando For
Each, para ello tendremos que recurrir a un serie de
asignaciones algo extrañas, pero... que al fin y al cabo nos
permita darle esa funcionalidad a nuestra clase.
También añadiremos otros métodos, tales como:
Clone, para poder hacer copias de la colección, pero no una
copia por referencia, sino una copia totalmente independiente de la
clase/colección, tal como hicimos con la clase cColega.
Exists, que devolverá un valor Verdadero o Falso, según un
elemento esté o no en la colección.
Clear, que eliminará todos los elementos de la colección y
la dejará preparada para usarla desde cero.
Además, vamos a añadir un método llamado Equals, que
permitirá comprobar si la un objeto del tipo de la colección es
igual a otro objeto de ese mismo tipo... esto es útil para poder
"comparar" dos objetos. Este mismo método se lo
añadiremos a la clase cColega.
Dicho todo esto y
antes de ver el código de la colección personalizada, vamos a
crear el método Equals de la clase cColega (la
cuestión es siempre hacerse de rogar... ¡este Guille!)
Equals,
un método que comprueba si dos objetos son "exactamente"
iguales.
Nota:
Aquí vamos a ver sólo lo que habría que añadir a la clase cColega,
el código anterior sigue siendo válido y es el mismo que el que
vimos en la entrega 37, con el
añadido del método Clone de la
entrega 39.
Para hacer las
cosas bien, vamos a empezar por crear un nuevo proyecto, al cual le
añadiremos una Referencia que nos será útil, sino obligatoria,
para cuando vayamos a crear la colección.
Abre el Visual Basic, crear un proyecto de tipo EXE,
(automáticamente se añadirá un formulario llamado Form1), añade
la clase cColega, (te recomendaría que hicieras una copia de la
clase anterior), y del menú Proyecto, selecciona Referencias... del
cuadro de diálogo que te muestra, busca el elemento "OLE
Automation" y pulsa en el checkBox para seleccionarlo, esta
referencia yo suelo añadirla en todos los proyectos. En la
siguiente imagen puedes ver el cuadro de diálogo y la referencia
que tenemos que seleccionar (recuerda que el idioma de mi Visual
Basic está en inglés, así que, seguramente lo que tu veas sea
diferente... por el idioma, ya que el resultado es el mismo).

Añadir OLE Automation en
Referencias del proyecto
Te recuerdo que esto que acabamos de hacer no tiene nada que ver con
el método Equals, sino para que nuestra clase-colección se pueda
usar con bucles For Each.
¡Ahora sí!
Veamos el código del método Equals, (realmente una función que
devolverá True o False, según los elementos comparados sea o no
iguales).
Primero veremos el código y después te lo explico para que no te
quede duda... aunque es muy simple, como podrás comprobar.
Public Function Equals(ByVal unColega As cColega) As Boolean
' Comprueba si el objeto pasado en el parámetro es igual a este objeto
Dim iguales As Boolean
'
' Si el tipo pasado no es del tipo cColega, no son iguales
If (TypeOf unColega Is cColega) Then
' Comprobar si cada una de las propiedades son iguales,
' al estar anidadas,
' sólo serán iguales si llega a la última comparación.
With unColega
If .email = Me.email Then
If .FechaNacimiento = Me.FechaNacimiento Then
If .Nombre = Me.Nombre Then
iguales = True
End If
End If
End If
End With
Else
' Esta asignación no es necesaria,
' ya que el valor por defecto de la variable iguales es False
iguales = False
End If
Equals = iguales
End Function
Veamos qué es lo
que hace esta función.
Como esta función comparará el objeto actual con otro del mismo
tipo, (en este caso del tipo cColega), el parámetro pasado debe ser
del tipo cColega. Fíjate que he usado ByVal, pero lo mismo da si se
usa ByRef, ya que el Visual Basic no permitirá que el parámetro
sea de otro tipo... si lo intentas, recibirás un error del tipo
TypeMismatch si el parámetro es un objeto, pero no del tipo
cColega, o bien un error indicándote que el parámetro debe ser un
objeto.
Aún así... es
decir, sabiendo que a esa función sólo llegarán objetos del tipo
cColega, he añadido una comparación que comprueba que realmente el
objeto es del tipo cColega, esto sólo es para que sepas cómo
comprobarías que el objeto sea de ese tipo... ya que es útil si
nos decidiéramos a cambiar el tipo del parámetro por otro más
genérico, ya sea Object o Variant, (preferiblemente Variant, si lo
quieres hacer "súper" genérico); de esta forma, el
Visual Basic no daría ningún error y será el código de la
función el que se encargue de comprobar si son o no iguales.
Esta sería la
forma de declarar la función, ya que el código contenido en ella
seguiría siendo el mismo que hemos visto hace unas líneas:
Public Function Equals(ByVal unColega As Variant) As Boolean
Es tu decisión
utilizar una u otra forma, todo depende de cómo quieras que el
Visual Basic actúe e incluso cómo de cuidadoso debe ser el
programador que utilice tu clase... si es el programador el que debe
tener en cuenta si el objeto pasado a la función es del tipo
correcto o no, utiliza la primera versión, pero si quieres
"curarte en salud" y que no se produzca un error porque
ese programador no ha sido lo suficientemente cuidados, utiliza la
segunda.
(Guille, creo que si le das tu opinión
personal... pues...)
Vale, ¿cual te recomiendo que uses? Yo me inclinaría por la
versión "más estricta", ya que se supone que ese método
sirve para comprobar si dos objetos del tipo cColega son iguales,
por tanto el que utilice nuestra clase será el que deba tener en
cuenta que así sea...
Pero por otro lado... (se nota que el Guille
es Géminis y eso de la doble personalidad...), creo que no
hay que dar por hecho que el programador será cuidadoso, por tanto,
en los ejemplos, usaremos la segunda versión, la que tiene el
parámetro del tipo Variant.
De esta forma, podremos hacer algo como esto:
(se supone que tenemos una variable a nivel de módulo llamada
mColega, que es del tipo cColega y que se ha instanciado
correctamente)
' comparar un objeto del tipo cColega con otro de otro tipo diferente
Dim otro As Variant
'
otro = "Pepe"
'
If mColega.Equals(otro) Then
MsgBox "Los dos objetos son iguales"
Else
MsgBox "Los dos objetos son diferentes"
End If
También podríamos
hacer esto, y funcionaría correctamente:
' comparar un objeto del tipo cColega con otro de otro tipo diferente
Dim otro As Variant
'
Set otro = mColega
'
If mColega.Equals(otro) Then
MsgBox "Los dos objetos son iguales"
Else
MsgBox "Los dos objetos son diferentes"
End If
En el primer caso
nos diría que son diferentes y en el segundo que son iguales.
Nota:
En el segundo ejemplo, en el que se asigna a la variable otro
el objeto del tipo cColega, también se podría usar la primera
versión de la función Equals.
Bien, ya que
sabemos cómo comprobar si dos objetos son iguales... (¡Eh!
¡Guille! que dijiste que ibas a explicar cómo funcionaba esto... y
te has enrollao tanto con lo de los tipos... que...)
...esto... pues sí... veamos cómo funciona todo el código...
Ya ha quedado claro para que usamos el TypeOf: para comprobar si el
objeto pasado en el parámetro es del tipo cColega, en caso de que
así sea, se comprueba cada una de las propiedades del objeto
unColega con el contenido en esta clase, (para ello usamos Me).
Como puedes comprobar, he anidado cada comparación dentro de otra,
de forma que sólo se comprobará la siguiente si la anterior ha
dado como resultado un valor verdadero. En caso de que alguna de
esas comparaciones "falle", es decir, la misma propiedad
de los dos objetos no sean iguales, el contenido de la variable iguales
seguirá teniendo el valor que Visual Basic le asigna por defecto,
en el caso de las variables de tipo Boolean, será False. Por tanto
la función devolverá un valor falso... cosa que se hace en la
última línea de la función al devolver el valor contenido en la
variable iguales.
Sólo decirte, que la parte Else de la comparación que comprueba si
unColega es del tipo cColega, no es necesaria, ya que le estamos
asignando el valor que ya tiene por defecto.
Por tanto, el
código completo de la función Equals es este:
Public Function Equals(ByVal unColega As Variant) As Boolean
' Comprueba si el objeto pasado en el parámetro es igual a este objeto
Dim iguales As Boolean
'
' Si el tipo pasado no es del tipo cColega, no son iguales
If (TypeOf unColega Is cColega) Then
' Comprobar si cada una de las propiedades son iguales,
' al estar anidadas,
' sólo serán iguales si llega a la última comparación.
With unColega
If .email = Me.email Then
If .FechaNacimiento = Me.FechaNacimiento Then
If .Nombre = Me.Nombre Then
iguales = True
End If
End If
End If
End With
End If
Equals = iguales
End Function
Ahora sí... ¡por
fin! podremos ver lo que realmente teníamos que ver...
Crear clases personalizadas
Vamos a empezar la
clase-colección desde el principio, para que no te pierdas ningún
detalle.
Para empezar, añade una nueva clase al proyecto, en la ventana de
propiedades, cámbiale el nombre para que sea cColegas.
Muestra la ventana del código y añade la siguiente declaración:
Option Explicit
Private mCol As Collection
El Option Explicit
se supone que ya estaría... si es que sigues mis consejos... y si
no es así... yo de ti me lo pensaría "forastero"
(musiquilla de spaghetti-western)
A continuación declaramos un objeto del tipo Collection que será
el que se encargue de contener los objetos de nuestra colección.
Con esta línea, simplemente declaramos la variable, pero no
instanciamos (o creamos) el objeto, eso lo haremos en el evento Class_Initialize,
el cual se ejecutará cuando se cree un nuevo objeto de nuestra
colección.
Por otro lado, cuando un objeto se destruye, se ejecuta el evento Class_Terminate,
por tanto será en ese procedimiento donde destruiremos nuestra
colección.
Veamos el código de esos dos procedimientos:
Private Sub Class_Initialize()
Set mCol = New Collection
End Sub
Private Sub Class_Terminate()
Set mCol = Nothing
End Sub
Es recomendable que
siempre crees los nuevos objetos de forma separada de la
declaración, ya que si declaras la variable de esta otra forma:
Private mCol As New Collection
Consigues lo mismo,
pero también añades "sobrecarga" o trabajo extra al
Visual Basic, ya que cada vez que se utilice la variable mCol,
el VB tendrá que comprobar si ya existe una instancia en memoria y
si no es así, tiene que crearla...
Para que lo comprendas mejor, si tenemos el siguiente código:
i = mCol.Count
el Visual Basic realmente haría lo
siguiente:
If mCol Is Nothing Then
Set mCol = New Collection
End If
i = mCol.Count
Es decir, comprueba
si ya está creada la colección y si no es así, crea una nueva
instancia, con lo cual, ¡cada vez que se vaya a usar el objeto,
Visual Basic tiene que comprobar si ya está creado el objeto en
memoria!
Creo que esto ya te lo he dicho antes, si no es así... apúntatelo
y que no se te olvide...
Una vez aclarado
estoy ya que tenemos el código que declara el objeto Collection que
usará nuestra clase-colección, empecemos por los métodos que
suelen incluirse en todas las colecciones.
Count, nos
indicará cuantos elementos hay en la colección,
la codificación de este método es bien simple, ya que simplemente
llamamos al método con el mismo nombre que incorpora la colección.
Este método (realmente es una función), devolverá cero si no hay
ningún elemento en la colección o la cantidad de elementos que
tengamos almacenados.
Veamos el código del método Count:
Public Function Count() As Long
Count = mCol.Count
End Function
Remove,
eliminará un elemento de la colección.
El código también es bastante simple, ya que utilizaremos el
método Remove del objeto mCol:
Public Sub Remove(ByVal Index As Variant)
mCol.Remove Index
End Sub
El parámetro lo
declaramos con el tipo Variant para que nos permita usar las dos
formas que las colecciones de Visual Basic permite:
1- Utilizar un índice numérico que será la posición del objeto
que queremos borrar, el primer elemento será el 1.
2- Utilizar la clave que hemos usado para el objeto que queremos
eliminar.
En el caso de que el elemento que queremos eliminar no esté en la
colección, se producirá un error, por tanto podríamos añadir
detección de errores, para evitar que eso ocurra. Pero, si no se
produce un error, el usuario de nuestra clase no sabrá que ese
elemento no estaba... y por tanto no "prestaría"
atención y podría creer que está trabajando de forma correcta,
por tanto vamos a dejar el código así, tal como está.
Si quieres crear una versión del
método Remove que tenga en cuenta lo que te acabo de comentar,
tendrás que declarar Remove como una función y si se produce un
error devolver un valor Falso y en caso de que se borre
correctamente el elemento indicado, devolver un valor Verdadero, de
forma que se pueda usar de la siguiente forma:
' oColegas es una variable del tipo cColegas
If oColegas.Remove(3) Then
Text1 = "Se ha borrado correctamente"
Else
Text1 = "No se ha borrado el elemento indicado"
End If
La forma de
declarar esta versión de Remove no te lo muestro, para que lo hagas
como "ejercicio".
Add,
para añadir nuevos elementos a la colección.
El método para añadir nuevos elementos a la colección lo podemos
escribir de varias formas:
1- Tal como se hace con las colecciones de Visual Basic:
mCol.Add Item, Key, before, after
2- Añadiendo un
objeto del tipo que la colección contendrá y opcionalmente
indicando la clave:
mCol.Add unColega
mCol.Add unColega, Key
Aunque esta forma de hacerlo sería
igual que el del punto 1, ya que en esa forma de hacerlo, el único
parámetro obligatorio es el primero y los demás opcionales.
3- Como se hace en
algunas de las colecciones de VB, en las que se indica algunos de
los valores de las propiedades y que devuelva un objeto del tipo
cColega.
Dim oColega As cColega
Set oColega = mColegas.Add("Guille", "guille@wcostasol.es")
El cual también
permitiría añadir elementos a la colección sin necesidad de
"recibir" el objeto:
mColegas.Add "otro Guille", "guille@mvps.org"
4- Una mezcla de
los otros:
-Si el primer parámetro es un objeto del tipo cColega
funcionará como lo indicado en el punto 2
-Si el primer parámetro NO es un objeto del tipo cColega,
supondremos que es el nombre.
De esta forma podríamos añadir objetos a la colección de
cualquiera de estas formas:
Set oColega = New cColega
oColega.Nombre = "el Guille"
oColega.email = "guille@costasol.net"
oColega.FechaNacimiento = "07/06/1957"
'
mColegas.Add oColega
mColegas.Add oColega, "guille@costasol.net"
mColegas.Add oColega, "guille@costasol.net", "07/06/1957"
mColegas.Add "el Guille", "guille@costasol.net"
mColegas.Add "el Guille", "guille@costasol.net", "07/06/1957"
Set oColega = mColegas.Add("el Guille")
Set oColega = mColegas.Add("el Guille", "guille@costasol.net")
Set oColega = mColegas.Add("el Guille", "guille@costasol.net", "07/06/1957")
Si después de
estos ejemplos no te sabes mi dirección de correo... en fin.
Es decir, podemos
crear nuevos objetos de formas muy variadas...
Pero lo interesante es saber ¿cómo se hace esto?
Y la verdad es que no se si sería conveniente mostrarte el
código... (venga Guille, no te hagas de rogar)
No es por hacerme de rogar ni aparentar "suspense", es que
puede ser que te líes... así que, lo mejor es mostrarlos todos,
para que vayas comprendiendo mejor las opciones. (y
así de camino la entrega es más larga... ¡no sabes ná!)
1- Al estilo del
objeto Collection de Visual Basic:
Public Sub Add(ByVal Item As cColega, _
Optional ByVal key As String, _
Optional ByVal before As Variant, _
Optional ByVal after As Variant)
mCol.Add Item, key, before, after
End Sub
Aquí lo que
hacemos es recibir cuatro parámetros, los tres últimos opcionales.
El primero debe ser del tipo cColega, ya que esta colección sólo
admitirá objetos de ese tipo.
2- Este es como el
anterior, pero sin los dos últimos parámetros, aunque el segundo
será opcional.
3- En esta forma de
hacerlo, los parámetros serán equivalentes a las propiedades del
objeto, por tanto se usarán para crear un nuevo objeto del tipo
cColega y ese nuevo objeto se añadirá a la colección y se
devolverá por la función.
Public Function Add(ByVal elNombre As String, _
ByVal elEmail As String, _
Optional ByVal laFecha As String) As cColega
Dim tColega As cColega
Set tColega = New cColega
'
With tColega
.email = elEmail
.FechaNacimiento = laFecha
.Nombre = elNombre
End With
' usamos el email como clave, para asegurarnos de que sea único
mCol.Add tColega, elEmail
' Devolver el objeto recién creado
Set Add = tColega
End Function
4- Por último, la
versión "multi-uso", que se podrá usar como cualquiera
de las dos anteriores:
Public Function Add(ByVal uno As Variant, _
Optional ByVal elEmail As String, _
Optional ByVal laFecha As String) As cColega
Dim tColega As cColega
'
' si el tipo del primer parámetro es un objeto del tipo cColega
If TypeOf uno Is cColega Then
Set tColega = uno
' si se indican los otros parámetros...
If Len(elEmail) > 0 Then
tColega.email = elEmail
End If
If Len(laFecha) > 0 Then
tColega.FechaNacimiento = laFecha
End If
Else
' sino, suponemos que el primer parámetro es el nombre
Set tColega = New cColega
'
With tColega
.email = elEmail
.FechaNacimiento = laFecha
.Nombre = uno
End With
End If
' usamos el email como clave, para asegurarnos de que sea único
mCol.Add tColega, tColega.email
'
' Devolver el objeto recién creado
Set Add = tColega
End Function
Para conseguir
nuestro objetivo, el primer parámetro será de tipo Variant, con
idea de que permita "cualquier" tipo de dato en ese
parámetro.
A continuación comprobamos si dicho parámetro es un objeto del
tipo cColega, de ser así, se asignará a la variable que hemos
creado dentro del procedimiento y opcionalmente se asignarán el
resto de los parámetros, siempre que éstos se hayan indicado con
algo diferente a una cadena vacía.
Si el primer parámetro NO es del tipo cColega, vamos a asumir que
es el valor que asignaremos a la propiedad Nombre del objeto
que queremos crear. En caso de usarla de esta segunda forma,
deberíamos especificar también el email, ya que es esa propiedad
la que usamos como clave del objeto, por la sencilla razón de que
no tendremos dos colegas con una misma cuenta de correo... Debido a
que es la propia clase cColega la que comprueba si lo que se asigna
a la propiedad email es una cuenta de correo, si ese segundo
parámetro no lo fuera, se produciría un error.
Item,
nos permitirá acceder a un elemento de la colección,
bien indicando el valor de la clave o una posición dentro de la
colección.
Por tanto podríamos acceder a un elemento de la colección de
cualquiera de estas dos formas:
mColegas.Item("guille@costasol.net").Nombre = "Guille"
mColegas.Item(2).Nombre = "Guille"
El código más
simple para esta función sería el siguiente:
Public Function Item(ByVal Index As Variant) As cColega
Set Item = mCol(Index)
End Function
El problema se
presentará si el elemento al que queremos acceder no existe en la
colección.
Pero eso podemos solventarlo con algunos de los métodos que
después añadiremos.
Lo que sería
interesante es poder usar este método sin tener que indicar la
palabra Item, tal como se hace en las colecciones propias de Visual
Basic, de forma que esas dos asignaciones pudiéramos hacerlas de
esta otra forma:
mColegas("guille@costasol.net").Nombre = "Guille"
mColegas(2).Nombre = "Guille"
Para conseguirlo,
tenemos que indicarle al Visual Basic que el método Item es el
método por defecto de la clase.
Para ello, tenemos que posicionarnos en la declaración del
procedimiento (aunque no es estrictamente necesario), ahora pulsa en
el menú Tools>Procedure Attributes... (Herramientas/Atributos de
procedimientos), tal como se muestra en esta imagen:

Del cuadro de
diálogo que se muestra, selecciona Item de la lista superior, pulsa
en el botón Avanzado para que se muestre la otra parte de la
ventana y en la lista ID de procedimiento selecciona (Default), tal
como se muestra en esta imagen:

Para que una propiedad sea la predeterminada
Y después de
pulsar Intro para aceptar las selecciones, el método Item será el
predeterminado y por tanto no será necesario indicarlo para usarlo.
Crear un
"enumerador" para nuestra colección.
Para terminar con la colección, (no te preocupes que no me he
olvidado de que hay más cosas, me refiero a terminar con los
métodos que se incluyen en las colecciones normales), vamos a crear
un método "especial" que permita recorrer los elementos
de la colección usando For Each.
Para conseguir nuestro objetivo, vamos a crear un método que se
llame NewEnum y que sea del tipo IUnknown, no voy a entrar en
detalles del "por qué", simplemente veremos el código y
la forma de "configurar" este procedimiento en el cuadro
de diálogo del atributos de procedimientos.
Empecemos por el código:
Public Function NewEnum() As IUnknown
' Debe ser un miembro oculto y
' el id del procedimiento debe ser -4
'
Set NewEnum = mCol.[_NewEnum]
End Function
Sólo aclararte que
los corchetes son necesarios ya que el nombre de la propiedad
contiene un carácter no válido, (empieza por un guión bajo),
éste indica que es un miembro oculto...
Pero sólo con esto no conseguimos nuestro objetivo, tal como se
indica en los comentarios, el procedimiento NewEnum debe estar
oculto y tener un ID de procedimiento con valor -4, esto lo hacemos
mediante el cuadro de diálogo usado para asignar la propiedad
predeterminada, tal como podemos ver en la siguiente imagen:

Para crear un enumerador que permita recorrer la
colección usando For Each
Una vez hecho esto, podemos
recorrer los elementos de la colección de esta forma:
' mostrar el contenido de la colección
Dim oColega As cColega
'
Text1 = ""
For Each oColega In mColegas
Text1 = Text1 & oColega.Nombre & ", " & _
oColega.email & ", " & _
oColega.FechaNacimiento & vbCrLf
Next
Aunque también podemos hacerlo de
la forma clásica:
Dim i As Long
'
Text1 = ""
For i = 1 To mColegas.Count
Text1 = Text1 & mColegas(i).Nombre & ", " & _
mColegas(i).email & ", " & _
mColegas(i).FechaNacimiento & vbCrLf
Next
Ampliando la
funcionalidad de la colección.
Bien, con lo visto hasta ahora, tenemos la misma funcionalidad que
la mayoría de las colecciones de Visual Basic.
Vamos a añadir nuevos métodos.
Clear,
eliminar el contenido de la colección.
Empezaremos con uno que nos permita eliminar el contenido de la
colección y así dejarla preparada para añadir nuevos elementos en
una colección totalmente vacía.
Si no tuviéramos este método, tendríamos que hacerlo de esta
forma:
Set mColegas = Nothing
Set mColegas = New cColegas
Y eso es
precisamente lo que haremos en el método Clear... es que es la
forma más rápida y "limpia" de eliminar los elementos,
pero por supuesto lo que borramos es el contenido de la colección
privada de nuestra clase-colección.
Public Sub Clear()
Set mCol = Nothing
Set mCol = New Collection
End Sub
Exists,
comprobar si un elemento está incluido en la colección.
Para comprobar si un elemento está en la colección podemos hacerlo
de dos formas, una sería recorriendo cada uno de los elementos de
la colección y comprobar si el indicado está contenido... pero hay
otra forma más fácil, y puede que más rápida, que es
aprovecharse de que se produce un error cuando se quiere acceder a
un elemento y dicho elemento no existe... veamos el código de esta
última forma:
Public Function Exists(ByVal Index As Variant) As Boolean
Dim tColega As cColega
'
On Error Resume Next
'
Set tColega = mCol(Index)
' si se produce un error es que no existe ese elemento
If Err.Number <> 0 Then
Exists = False
Else
Exists = True
End If
End Function
Nota:
A este método también podríamos llamarlo Contains, (que es como
suele llamarse en las colecciones de la nueva versión .NET de
Visual Basic).
Clone,
crear una copia "independiente" de la colección.
Tal como hicimos con la clase cColega, vamos a crear un método que
permita hacer una copia independiente de la colección.
Te recuerdo que cuando asignamos un objeto a otro, lo único que
conseguimos es crear una nueva referencia que apunta al mismo objeto
que ya está creado en la memoria, es decir sólo existe un
objeto en la memoria.
Pero mediante este método creamos otra copia en la memoria, de
forma que los cambios realizados en ella no afectarán al original.
El código es bien simple, ya que vamos a aprovecharnos de que la
clase cColega ya tiene un método que permite crear copias de ese
objeto.
Veamos el código:
Public Function Clone() As cColegas
' Hacer una copia de esta colección
'
Dim tColega As cColega
Dim tColegas As cColegas
'
Set tColegas = New cColegas
'
' Añadir a la nueva colección una copia de cada elemento
For Each tColega In mCol
tColegas.Add tColega.Clone()
Next
'
Set Clone = tColegas
End Function
Lo que hacemos es
crear una nueva colección del mismo tipo de la clase, recorremos
cada uno de los elementos contenidos y añadimos una copia, con idea
de que se cumpla nuestro objetivo, tener copias independientes.
Y para terminar (¡ya
era hora!), nos queda por ver cómo sería el código para el
método para saber si dos colecciones son iguales:
Equals,
saber si dos colecciones son iguales.
El código de este método es algo diferente al de la clase normal,
entre otras cosas porque no sólo tenemos que comprobar si las
propiedades de cada uno de los elementos son exactamente iguales,
(aunque de ese detalle se encarga el método Equals de cada uno de
los objetos contenidos en la colección), sino que también tenemos
que tener en cuenta otras cosas... aunque al fin y al cabo todo es
para poder saber si las dos colecciones son iguales...
Veamos el código:
Public Function Equals(ByVal compararCon As Variant) As Boolean
Dim iguales As Boolean
Dim oColegas As cColegas
Dim oColega As cColega
'
' Sólo serán iguales si es un objeto del tipo cColegas
If TypeOf compararCon Is cColegas Then
Set oColegas = compararCon
' sólo serán iguales si tienen los mismos elementos
If oColegas.Count = mCol.Count Then
' asumimos que serán iguales
iguales = True
For Each oColega In mCol
' si no existe o no son iguales...
If oColegas.Exists(oColega.email) = False Then
iguales = False
Exit For
ElseIf oColega.Equals(oColegas(oColega.email)) = False Then
iguales = False
Exit For
End If
Next
End If
End If
Equals = iguales
End Function
Lo primero que
hacemos es comprobar que el objeto es del tipo adecuado, si es así,
asignamos ese objeto a una variable del tipo cColegas, (esto
simplemente es para poder usar las propiedades y métodos, ya que si
usamos la variable pasada como parámetro, al ser del tipo Variant,
no nos mostraría los miembros).
A continuación comprobamos que el número de elementos de las dos
colecciones sean iguales, en caso de que no lo sean, el valor de la
variable iguales no cambiará, con lo cual la función
devolverá un valor False.
A partir de este punto asignamos un valor verdadero a esa variable,
esto es así porque las siguientes comparaciones buscan valores que
diferencien a ambas colecciones... Para ello, hacemos un bucle que
recorra todos los elementos, comprobamos si cada uno de los
elementos existe en la otra colección, si no es así, asignamos un
valor falso y salimos del bucle. Si el email es igual en los dos
objetos, comprobamos si realmente son iguales, en caso de que no lo
sean, asignamos un valor falso y salimos del bucle.
Puede que pienses que esto se podría haber simplificado usando
sólo esta comparación:
If oColega.Equals(oColegas(oColega.email)) = False Then
iguales = False
Exit For
End If
Pero esto daría
error si en la colección pasada por parámetro no contiene el
objeto que se comprueba.
¿Por qué?
Como hemos visto, si hacemos esto: oColegas(laClave), que es lo
mismo que si hacemos esto otro: oColegas.Item(laClave), y dicha
clave no está contenida en la colección, se producirá un error,
ya que en el método Item de la colección no se hace ningún tipo
de detección de errores, por tanto si el elemento al que se quiere
acceder no existe, se produce un error. (Guille
eso lo dijiste antes..., ya lo sé,
pero así seguro que queda más claro...)
Por esa razón se hace primero la comprobación de que exista el
objeto en la colección.
Y hasta aquí hemos
llegado en esta entrega que tanto se ha hecho esperar... aunque
confío que con lo que me he enrollado, no te quejes y espero que
tampoco te "empaches" demasiado... je, je.
Posiblemente,
en la próxima entrega veremos más cosas referentes a las clases...
pero no nos precipitemos que si después cambio de idea... lo mismo
me regañas...
Nos vemos
P.S.
Aquí tienes el código completo
de las clases y un proyecto de prueba: basico42_cod.zip 6.01 KB
P.S.2:
Aquí te explico "someramente" lo que es eso del For Each,
ya que según parece sólo lo dije "de pasada" en la
entrega 38.
For Each,
otra forma de hacer bucles.
Este tipo de
bucles, a diferencia del clásico bucle For al que hay que indicarle
una variable numérica, se utiliza con una variable de un tipo de
objeto que debe estar contenido en una colección, de forma que se
pueda recorrer cada uno de los elementos de dicha colección que
sean del tipo usado junto a For Each.
La forma de usarla sería:
For Each elemento In
laColección
' lo que haya que hacer con
el elemento
Next
...

|