índice del curso de VB .NET

Curso de iniciación a la programación
con Visual Basic .NET

Entrega número diez, (19/Ene/2003)
Publicada el 19/Ene/2003


En esta primera entrega del recién estrenado año 2003 del curso de iniciación a la programación con Visual Basic .NET seguiremos tratando el tema de los arrays o matrices.

En la entrega anterior vimos los arrays de una sola dimensión, que, por cierto, son las que con mayor asiduidad usaremos en nuestras aplicaciones, pero el tema que vamos a tratar hoy sigue siendo también los arrays, pero con un matiz especial, ya que son las llamadas arrays (o matrices) multidimensionales, es decir arrays que no sólo tienen un índice simple, sino que serán como tablas, es decir, arrays que se pueden representarse por medio de filas y columnas, además de, si se le echa un poco de imaginación, algún otro nivel dimensional... vale, espera y sabrás a que me refiero.

Tal y como vimos en la entrega anterior, la diferencia entre las variables "normales" y los arrays era que se utilizaba un índice para acceder al contenido de cada una de las variables normales contenidas en un array. Por ejemplo, para acceder al tercer elemento de un array llamado nombres, tendríamos que hacer algo como esto: nombres(2). Recuerda que los arrays empiezan con un índice cero, por tanto el índice 2 será el tercero.


Los arrays multidimensionales

La diferencia entre los arrays unidimensionales y los arrays multidimensionales es precisamente eso, que los arrays unidimensionales, (o normales, por llamarlos de alguna forma), sólo tienen una dimensión: los elementos se referencian por medio de un solo índice. Por otro lado, los arrays multidimensionales tienen más de una dimensión. Para acceder a uno de los valores que contengan habrá que usar más de un índice. La forma de acceder, por ejemplo en el caso de que sea de dos dimensiones, sería algo como esto: multiDimensional(x, y).
Es decir, usaremos una coma para separar cada una de las dimensiones que tenga. El número máximo de dimensiones que podemos tener es de 32, aunque no es recomendable usar tantas, (según la documentación de Visual Studio .NET no deberían usarse más de tres o al menos no debería ser el caso habitual), en caso de que pensemos que debe ser así, (que tengamos que usar más de tres), deberíamos plantearnos usar otro tipo de datos en lugar de una matriz. Entre otras cosas, porque el Visual Basic reservará espacio de memoria para cada uno de los elementos que reservemos al dimensionar un array, con lo cual, algo que puede parecernos pequeño no lo será tanto.
Hay que tener en cuenta que cuando usamos arrays de más de una dimensión cada dimensión declarada más a la izquierda tendrá los elementos de cada una de las dimensiones declaradas a su derecha. De esto te enterarás mejor en cuanto te explique cómo crear los arrays multidimensionales... que me pongo a explicar cosas y lo mismo ni te estás enterando... (menos mal que el Guille se ha dado cuenta y no le he tenido que llamar la atención... la verdad es que estaba despistadillo escuchando música y... en fin... espero estar más al tanto de los deslices del Guille para que no te confunda o líe más de la cuenta...)


Declarar arrays multidimensionales

Para declarar un array multidimensional, lo podemos hacer, (al igual que con las unidimensionales), de varias formas, dependiendo de que simplemente declaremos el array, que le indiquemos (o reservemos) el número de elementos que tendrá o de que le asignemos los valores al mismo tiempo que la declaramos, veamos ejemplos de estos tres casos:

Dim a1() As Integer
Dim a2(,) As Integer
Dim a3(,,) As Integer
Dim b1(2) As Integer
Dim b2(1, 6) As Integer
Dim b3(3, 1, 5, 2) As Integer
Dim c1() As Integer = {1, 2, 3, 4}
Dim c2(,) As Integer = {{1, 2, 3}, {4, 5, 6}}
' este array se declararía como c3(3, 2, 1)
Dim c3(,,) As Integer = { _
                         {{1, 2}, {3, 4}, {5, 6}}, _
                         {{7, 8}, {9, 10}, {11, 12}}, _
                         {{13, 14}, {15, 16}, {17, 18}}, _
                         {{19, 20}, {21, 22}, {23, 24}} _
                        }

En estos ejemplos he usado arrays con una, dos y tres dimensiones.
En el último caso, he usado el continuador de líneas para que sea más fácil "deducir" el contenido de cada una de las dimensiones... imagínate que en lugar de tres dimensiones hubiese usado más... sería prácticamente imposible saber cuantos elementos tiene cada una de las dimensiones. Recuerda que cuando declaramos con a(1) realmente tenemos dos elementos el cero y el uno.


El tamaño de un array multidimensional

En la entrega anterior utilizamos la propiedad Length para averiguar el tamaño de un array de una sola dimensión, en el caso de los arrays de varias dimensiones, se puede seguir usando Length para saber el número total de elementos que contiene, (será la suma de todos los elementos en su totalidad), ya que Length representa la longitud o tamaño, pero para saber cuantos elementos hay en cada una de las dimensiones tendremos que usar otra de las propiedades que exponen los arrays: GetUpperBound(dimensión).
Esta propiedad se usará indicando como parámetro la dimensión de la que queremos averiguar el índice superior.
Por ejemplo, en el caso del array c3, podemos usar c3.GetUpperBound(2) para saber cuantos elementos hay en la tercera dimensión (la del array, no la de las novelas y pelis de SCI-FI).

Veamos cómo haríamos para averiguar cuantos elementos tiene cada una de las tres dimensiones del array c3:

Console.WriteLine("Las dimensiones de c3 son: (?,,)= {0}, (,?,)= {1}, (,,?)= {2}", _
                  c3.GetUpperBound(0), c3.GetUpperBound(1), c3.GetUpperBound(2))


El número de dimensiones de un array multidimensional.

Una cosa es saber cuantos elementos tiene un array (o una de las dimensiones del array) y otra cosa es saber cuantas dimensiones tiene dicho array.
Para saber el número de dimensiones del array, usaremos la propiedad Rank.
Por ejemplo, (si usamos la declaración hecha anteriormente), el siguiente código nos indicará que el array c3 tiene tres dimensiones:
Console.WriteLine("EL array c3 tiene {0} dimensiones.", c3.Rank)

Como siempre, el valor devuelto por Rank será el número total de dimensiones del array, pero ese número de dimensiones será desde cero hasta Rank - 1.

 

Veamos ahora un ejemplo de cómo recorrer todos los elementos del array c3, los cuales se mostrarán en la consola. Para saber cuantos elementos hay en cada una de las dimensiones, utilizaremos la propiedad GetUpperBound para indicar hasta qué valor debe contar el bucle For, el valor o índice menor sabemos que siempre será cero, aunque se podría usar la propiedad GetLowerBound.

Nota:
Esto es curioso, si todos los arrays empiezan por cero, ¿qué sentido tiene poder averiguar el valor del índice menor? ya que, según sabemos siempre debe ser cero. Lo mismo es que han dejado la puerta abierta a un posible cambio en esta "concepción" del índice menor de los arrays... en fin... el tiempo (y las nuevas versiones de .NET Framework) lo dirá.
Si has trabajado anteriormente con Visual Basic clásico, sabrás que en VB6 podemos indicar "libremente" el valor inferior así como el superior de un array y el uso del equivalente a GetLowerBound si que tenía sentido.

Dejemos estas cábalas y veamos el ejemplo prometido:

Dim i, j, k As Integer

For i = 0 To c3.GetUpperBound(0)
    For j = 0 To c3.GetUpperBound(1)
        For k = 0 To c3.GetUpperBound(2)
            Console.WriteLine("El valor de c3({0}, {1}, {2}) es {3}", i, j, k, c3(i, j, k))
        Next
    Next
Next

Seguramente te estarás preguntando qué son todos esas llaves con "numericos" que está usando el Guille en los ejemplos... no te preocupes que dentro de poco te lo explico con un poco de detalle, aunque espero que sepas "deducir" para qué sirven... al menos si estás probando estos "trozos" de ejemplos.

 


Cambiar el tamaño de un array y mantener los elementos que tuviera.

Esto es algo que debería haber explicado en la entrega anterior, pero seguramente se me "escapó", (venga va, perdonemos al despistado del Guille, que demasiado hace... pa lo despistao que es...), así que, para que la próxima sección no te suene a "chino", voy a explicarte cómo poder cambiar el tamaño de un array sin perder los valores que tuviese antes.
Para poder conseguirlo, debemos usar ReDim seguida de la palabra clave Preserve, por tanto si tenemos la siguiente declaración:
Dim a() As Integer = {1, 2, 3, 4, 5}
Y queremos que en lugar de 5 elementos (de 0 a 4) tenga, por ejemplo 10 y no perder los otros valores, usaremos la siguiente instrucción:
ReDim Preserve a(10)
A partir de ese momento, el array tendrá 11 elementos (de 0 a 10), los 5 primeros con los valores que antes tenía y los nuevos elementos tendrán un valor cero, que es el valor por defecto de los valores numéricos.

Si sólo usamos ReDim a(10), también tendremos once elementos en el array, pero todos tendrán un valor cero, es decir, si no se usa Preserve, se pierden los valores contenidos en el array.

 


Redimensionar un array multidimensional.

En la entrega anterior vimos que usando ReDim podemos cambiar el número de elementos de un array, e incluso que usando ReDim Preserve podemos cambiar el número de elementos y mantener los que hubiese anteriormente en el array, (esto último no lo busques en la entrega anterior, ya que se ha aclarado hace unas pocas líneas). Con los arrays multidimensionales también podemos usar esas instrucciones con el mismo propósito que en los arrays unidimensionales.

El único problema con el que nos podemos encontrar, al menos si queremos usar Preserve para conservar los valores previos, es que sólo podemos cambiar el número de elementos de la última dimensión del array. Si has usado el VB6 esto es algo que te "sonará", ya que con el VB clásico tenemos el mismo inconveniente, pero a diferencia de aquél, con ReDim podemos cambiar el número del resto de las dimensiones, al menos la cantidad de elementos de cada dimensión, ya que si un array es de 3 dimensiones siempre será de tres dimensiones.

Con esto último hay que tener cuidado, ya que si bien será el propio IDE el que nos avise de que no podemos cambiar "la cantidad" de dimensiones de un array, es decir, si tiene tres dimensiones, siempre debería tener tres dimensiones, por ejemplo, siguiendo con el ejemplo del array c3, si hacemos esto:
ReDim c3(1, 4)
Será el IDE de Visual Studio .NET el que nos avise indicando con los dientes de sierra que hay algo que no está bien.
Pero si cambiamos el número de elementos de las dimensiones (usando Preserve), hasta que no estemos en tiempo de ejecución, es decir, cuando el programa llegue a la línea que cambia el número de elementos de cada dimensión, no se nos avisará de que no podemos hacerlo.

Veamos qué podemos hacer sin problemas y que daría error:
Si tenemos c3 dimensionada con tres dimensiones, al estilo de Dim c3(3, 2, 1):
ReDim c3(3, 2) dará error en tiempo de diseño.
ReDim c3(2, 3, 4) funcionará bien.
ReDim Preserve c3(3, 3, 1) en tiempo de ejecución nos dirá que nones... que no se puede.
ReDim Preserve c3(3, 2, 4) será correcto y los nuevos elementos tendrán el valor por defecto.
ReDim Preserve c3(3, 2, 0) será correcto, hemos reducido el número de elementos de la última dimensión.

Aclarando temas:
Podemos usar ReDim para cambiar el número de elementos de cada una de las dimensiones, pero no podemos cambiar el número de dimensiones.
Podemos usar ReDim Preserve para cambiar el número de elementos de la última dimensión sin perder los valores que previamente hubiera.
En ningún caso podemos cambiar el número de dimensiones de un array.

 


Eliminar un array de la memoria.

Si en algún momento del programa queremos eliminar el contenido de un array, por ejemplo para que no siga ocupando memoria, ya que es posible que no siga ocupando memoria, podemos usar Erase seguida del array que queremos "limpiar", por ejemplo:
Erase a
Esto eliminará el contenido del array a.

Si después de eliminar el contenido de un array queremos volver a usarlo, tendremos que ReDimensionarlo con el mismo número de dimensiones que tenía, ya que Erase sólo borra el contenido, no la definición del array.

 


¿Podemos clasificar un array multidimensional?

Pues la respuesta es: No.
El método Sort sólo permite clasificar un array unidimensional, (de una sola dimensión).
La única forma de clasificar un array multidimensional sería haciéndolo de forma manual, pero esto es algo que, sintiéndolo mucho, no te voy a explicar.

De igual forma que no podemos clasificar un array multidimensional, al menos de forma "automática", tampoco podemos usar el resto de métodos de la clase Array que se suelen usar con arrays unidimensionales, como puede ser Reverse.


Copiar un array multidimensional en otro.

Para copiar el contenido de un array, sea o no multidimensional, podemos usar el método Copy de la clase Array.
Seguramente te preguntarás si se puede usar CopyTo, que es la forma que vimos en la entrega anterior para copiar el contenido de un array, la respuesta es NO, simplemente porque CopyTo sólo se puede usar con arrays unidimensionales.

Nota:
Que no te sorprenda que, en el IDE, al mostrar los miembros (métodos y propiedades) de un array multidimensional se muestren métodos no válidos para los arrays multidimensionales, así que, acostúmbrate a "leer" el ToolTip (mensaje de ayuda emergente) que se muestra o, mejor aún, leer la documentación de Visual Studio .NET.

Para usar el método Copy de la clase Array, debemos indicar el array de origen, el de destino y el número de elementos a copiar. Siguiendo con el ejemplo del array c3, podríamos copiar el contenido en otro array de la siguiente forma:

Dim c31(,,) As Integer
'
ReDim c31(c3.GetUpperBound(0), c3.GetUpperBound(1), c3.GetUpperBound(2))
Array.Copy(c3, c31, c3.Length)

Fíjate que no se indica qué dimensión queremos copiar, ya que se copia "todo" el contenido del array, además de que el array de destino debe tener como mínimo el mismo número de elementos.
Otra condición para poder usar Copy es que los dos arrays deben tener el mismo número de dimensiones, es decir, si el array origen tiene 3 dimensiones, el de destino también debe tener el mismo número de dimensiones.
Si bien, el número de dimensiones debe ser el mismo en los dos arrays, el número de elementos de cada una de las dimensiones no tiene porqué serlo. Sea como fuere, el número máximo de elementos a copiar tendrá que ser el del array que menos elementos tenga... sino, tendremos una excepción en tiempo de ejecución.

Para entenderlo mejor, veamos varios casos que se podrían dar. Usaremos el contenido del array c3 que, como sabemos, está definido de esta forma: c3(3, 2, 1) y como destino un array llamado c31.
Para copiar los elementos haremos algo así:
Array.Copy(c3, c31, c3.Length)

Ahora veamos ejemplos de cómo estaría dimensionado el array de destino y si son o no correctos:
Dim c31(3, 2, 1), correcto ya que tiene el mismo número de dimensiones y elementos.
Dim c31(3, 3, 2), correcto porque tiene el mismo número de dimensiones y más elementos.
Dim c31(2, 1, 0), correcto, tiene el mismo número de dimensiones aunque tiene menos elementos, por tanto la cantidad de elementos a copiar debe ser el del array de destino: Array.Copy(c3, c31, c31.Length).
Dim c31(3, 2), no es correcto porque tiene un número diferente de dimensiones.

 

Espero que todo lo que hemos tratado sobre los arrays o matrices que hemos visto en esta dos últimas entregas sea suficiente para que sepas cómo usarlas, pero te repito y a pesar de que pienses que pueda ser un "pesado", te recomiendo que practiques, practiques y sigas practicando, eso unido a que leas la documentación de Visual Studio .NET, hará que te enteres bien de cómo usarlas.
También te digo que, seguramente, te encontrarás con algunas cosas en la ayuda que, salvo que lo hayas aprendido por tu cuenta, te resultarán extrañas y puede que no las entiendas... paciencia te pido, ya que aún quedan muchos conceptos que debemos aprender y, como es natural no se pueden resumir en 10 entregas.
No te voy a decir que es lo que puedes encontrarte en la documentación que no entiendas... puede que sean muchas... pero, ciñéndonos en el tema de los arrays, hay algunos conceptos referidos a la herencia y otras cosas relacionadas con la programación orientada a objetos que de seguro no te serán familiares.
Así que, puede ser que en la próxima entrega veamos el tema de la programación orientada a objetos, así que... ve preparando el cuerpo y, sobre todo, la mente para que cuando llegue ese día, que con total seguridad será el próximo mes de febrero (siempre que estés leyendo esto cuando se publica), no te de ningún tipo de "espasmo" o ataque "cerebral".

 

De todas formas, aún puedes descansar la mente unos minutos para que veamos de que va eso de las llaves con los números ({0}) que he usado en algunos de los ejemplos de esta y otras entregas anteriores.


Los formatos a usar con las cadenas de Console.Write y WriteLine.

Aunque seguramente lo usarás en pocas ocasiones, ya que me imagino que la mayoría de las aplicaciones que hagas serán de tipo "gráficas" en lugar de aplicaciones de consola, te voy a explicar cómo poder dar "formato" a las cadenas usadas con los métodos Write y WriteLine de la clase Console.
Con esos métodos podemos indicar, entre otras cosas, una cadena que se mostrará en la ventana de consola, dentro de esa cadena podemos usar unos indicadores o marcadores en los que se mostrarán los parámetros indicados en esos dos métodos de la clase Console, los cuales también están presentes en otras clases del .NET Framework.

Nota:
El formato especificado en la cadena usada en los métodos Write y WriteLine será el mismo que se puede usar con el método Format de la clase String, por tanto, lo aquí explicado será aplicable a cualquier cadena.

Los marcadores se indican con un número encerrado entre llaves, los números indicarán el número u orden del parámetro que siga a la cadena en el que está ese marcador. Como es habitual, el primer parámetro estará representado por el cero y así sucesivamente.
Por ejemplo, para mostrar los valores de dos parámetros usaremos algo como esto:
Console.WriteLine("{0} {1}", i, j)
En este ejemplo, el {0} se sustituirá por el valor de la variable i, y el {1} por el valor de la variable j.

Cuando se indican marcadores, estos deben coincidir con el número de parámetros, es decir, si usamos el marcador {3}, debería existir ese parámetro, en caso de que haya menos de 4 parámetros (de 0 a 3), se producirá una excepción.

Nota:
Los parámetros pueden ser numéricos, de cadena, e incluso objetos basados en una clase. En todos los casos se usará la "versión" de tipo cadena de esos parámetros, de esto sabrás un poco más cuando toquemos el tema de las clases... sólo te lo comento para que no te extrañe ver algo "raro" si haces tus propias pruebas.

Con uno de estos marcadores, además de indicar el número de parámetro que se usará, podemos indicar la forma en que se mostrará: cuantos caracteres se mostrarán y si se formatearán a la derecha o la izquierda; también se pueden indicar otros valores de formato.
La forma de usar los marcadores o las especificaciones de formato será la siguiente:
{ N [, M ][: Formato ]}
N será el número del parámetro, empezando por cero.
M será el ancho usado para mostrar el parámetro, el cual se rellenará con espacios. Si M es negativo, se justificará a la izquierda, y si es positivo, se justificará a la derecha.
Formato será una cadena que indicará un formato extra a usar con ese parámetro.

Veamos algunos ejemplos para aclarar todo esto:

Console.WriteLine("---------- ----------")
Console.WriteLine("{0,10} {1,-10} {2}", 10, 15, 23)
'
Console.WriteLine("---------- ----------")
Console.WriteLine("{0,10:#,###.00} {1,10}", 10.476, 15.355)

El resultado de este ejemplo sería este:

---------- ----------
        10 15         23
---------- ----------
     10,48     15,355

En el primer caso, el primer número se muestra con 10 posiciones, justificado a la derecha, el segundo se justifica a la izquierda.
En el segundo caso, el número 10.476 se justifica también con 10 posiciones a la derecha, pero el número se muestra con un formato numérico en el que sólo se muestran dos decimales, por eso se ha redondeado el resultado, sin embargo en el segundo valor se muestran todos los decimales, ya que no se ha especificado ningún formato especial.

Estas especificaciones de formato es aplicable a números, pero también lo es a cadenas de caracteres y fechas. Veamos algunos ejemplos más:

Console.WriteLine("Hoy es: {0:G}", DateTime.Now)
Console.WriteLine("Hoy es: {0:dd/MM/yy}", DateTime.Now)
Console.WriteLine("Hoy es: {0:ddd, dd/MMM/yyyy}", DateTime.Now)

El resultado de estas líneas será el siguiente (al menos usando la configuración regional que tengo en mi equipo):

Hoy es: 19/01/2003 02:51:35
Hoy es: 19/01/03
Hoy es: dom, 19/ene/2003

Como puedes comprobar, en esta ocasión no se indica cuantos caracteres se usan para mostrar los datos, (ya que sólo se indica el parámetro y el formato a usar), pero en el caso de que se especifiquen, si la cantidad de caracteres a mostrar es mayor que los indicados, no se truncará la salida, es decir, no se quitarán los caracteres que sobren. Esto lo podemos ver en el siguiente ejemplo:

Dim s As String = "Esta es una cadena larga de más de 20 caracteres"
Dim s1 As String = "Cadena más corta"
'
Console.WriteLine("--------------------")
Console.WriteLine("{0,20}", s)
Console.WriteLine("{0,20}", s1)
Console.WriteLine("--------------------")

La salida de este código es la siguiente:

--------------------
Esta es una cadena larga de más de 20 caracteres
    Cadena más corta
--------------------

Tal como podemos comprobar, la primera cadena excede los 20 caracteres (los indicados con las rayas), por tanto se muestra todo el contenido, sin embargo la segunda cadena mostrada si se posiciona correctamente con espacios a la izquierda hasta completar los 20 especificados.

En formato se pueden usar muchos caracteres diferentes según el tipo de datos a "formatear", la lista sería bastante larga y ya que está disponible en Visual Studio .NET no voy a repetirla, de todas formas, con toda seguridad, veremos algunos ejemplos a lo largo de este curso de iniciación.
Para ver una lista detallada y bastante extensa, puedes buscar en la ayuda la función Format, que es propia de Visual Basic, también encontrarás más información sobre los formatos en otras partes de la documentación, pero es en esa función donde están más detallados los diferentes formatos a usar.

 

Pues creo que con esto acabamos esta décima entrega en la que además de ver cómo se trabaja con arrays multidimensionales, hemos visto algunos de los métodos usados con la clase Array, así como la forma de dar formatos a las cadenas usadas con los métodos Write y WriteLine de la clase Console.

En la próxima entrega, salvo que cambie de opinión a última hora, cuando vaya a escribirla, veremos o empezaremos a ver temas relacionados con la programación orientada a objetos, al menos en sus conceptos más básicos y elementales, los cuales iremos ampliando en otras entregas.
Creo que es muy importante saber sobre esos temas, ya que todo en .NET Framework y por tanto en Visual Basic .NET, está estrechamente relacionado con la programación orientada a objetos, así que... lo que te dije, ve dejando espacio libre en tu "cabecita" para que pueda entrar todo lo referente a ese tema. Aunque no te asustes si te parece que puede que sea algo demasiado "sofisticado" o avanzado, ya que, como lo trataremos bastante en todo este curso de iniciación, al final acabarás aprendiéndolo, ¡aunque no quieras!


Nos vemos.
Guillermo
Nerja, del 15 al 19 de Enero de 2003


ir a la entrega anterior ir al índice del curso vb.NET
 
ir al Glosario .NET
ir a la siguiente entrega (o al índice si esta es la última)

la Luna del Guille o... el Guille que está en la Luna... tanto monta...