Buscador

Tipos de tratamiento de error en VB.NET

VB.NET proporciona dos tipos de tratamiento de error: estructurado y no estructurado. El primero se basa en los esquemas de captura de errores de lenguajes como C# y C++; gestionando los errores a través de excepciones, y una estructura de control que se encarga de atrapar aquellas excepciones que se produzcan. 
El segundo es un sistema heredado de versiones anteriores del lenguaje Visual Basic, y está basado en la detección y captura de errores a través de etiquetas de código, mediante saltos no estructurados en el flujo de la ejecución.

Manipuladores de excepciones

Un manipulador de excepción es un bloque de código que proporciona una respuesta al error que se ha producido, y que se incluye en una estructura proporcionada por el lenguaje a tal efecto, es decir, para la captura de excepciones.

Manipulación de errores

Errores y excepciones 

Dentro del esquema de gestión de errores del entorno .NET Framework, encontramos las figuras del error y la excepción. Estos elementos son utilizados indistintamente en muchas ocasiones para hacer referencia genérica a los errores producidos; sin embargo, aunque complementarios, cada uno tiene su propia funcionalidad dentro del proceso de tratamiento de un error. 
  • Error. Un error es un evento que se produce durante el funcionamiento de un programa, provocando una interrupción en su flujo de ejecución. Al producirse esta situación, el error genera un objeto excepción. 
  • Excepción. Una excepción es un objeto generado por un error, que contiene información sobre las características del error que se ha producido.

Arrays multidimensionales

Para recorrer arrays multidimensionales, la clase Array dispone de varios miembros, algunos de los cuales describimos seguidamente. 
• Rank. Devuelve el número de dimensiones del array. 
• GetLength(Dimension). Devuelve el número de elementos de la dimensión del array pasada como parámetro. 
• GetLowerBound(Dimension). Devuelve el número de índice inferior de la dimensión pasada como parámetro. 
• GetUpperBound(Dimension). Devuelve el número de índice superior de la dimensión pasada como parámetro. 
El Código fuente 349 muestra un ejemplo de manipulación de un array multidimensional mediante las propiedades y métodos mencionados.

Sub Main()
' crear array multidimensional y rellenar de valores
Dim iDatos(2, 4) As Integer
iDatos(0, 0) = 1000
iDatos(0, 1) = 2000
iDatos(0, 2) = 3000
iDatos(0, 3) = 4000
iDatos(0, 4) = 5000
iDatos(1, 0) = 25
iDatos(1, 1) = 35
iDatos(1, 2) = 45
iDatos(1, 3) = 55
iDatos(1, 4) = 65
iDatos(2, 0) = 111
iDatos(2, 1) = 222
iDatos(2, 2) = 333
iDatos(2, 3) = 444
iDatos(2, 4) = 555
Dim iContadorDimUno As Integer
Dim iContadorDimDos As Integer
Dim sTextoFila As String
' poner títulos de la fila y columnas del array a mostrar
Console.WriteLine("Fila" & ControlChars.Tab & _
"Col 0" & ControlChars.Tab & "Col 1" & ControlChars.Tab & _
"Col 2" & ControlChars.Tab & "Col 3" & ControlChars.Tab & "Col 4")
' el bucle externo recorre la primera dimensión
For iContadorDimUno = iDatos.GetLowerBound(0) To iDatos.GetUpperBound(0)
' aquí obtenemos el número de fila
' que se está procesando
sTextoFila = iContadorDimUno & ControlChars.Tab
' este bucle recorre la segunda dimensión
For iContadorDimDos = iDatos.GetLowerBound(1) To iDatos.GetUpperBound(1)
sTextoFila = sTextoFila & iDatos(iContadorDimUno, iContadorDimDos) & _
ControlChars.Tab
Next
' mostrar en la consola el contenido
Console.WriteLine(sTextoFila)
sTextoFila = ""
Next
Console.WriteLine()
Console.WriteLine("El número de dimensiones es: {0}", iDatos.Rank)
Console.WriteLine("El número total de elementos es: {0}", iDatos.Length)
Console.ReadLine()
End Sub
Código fuente 349

Búsqueda

Los métodos IndexOf( ) y LastIndexOf( ) de la clase Array, nos permiten buscar un elemento en un array comenzando la búsqueda desde el principio o final respectivamente. Ya que ambos disponen de diferentes implementaciones al estar sobrecargados, consulte el lector la documentación de la plataforma. El Código fuente 348 muestra algunos ejemplos de uso.

Sub Main()
Dim sNombres() As String = {"Alberto", "Juan", "Ana", "Paco", "Miguel", "Ana"}
' buscar una cadena a partir del índice 0 del array
Console.WriteLine("Paco está en la posición {0}", _
Array.IndexOf(sNombres, "Paco"))
' buscar una cadena a partir del índice 3 del array
Console.WriteLine("Ana está en la posición {0}," & _
" comenzando a buscar desde índice 3", _
Array.IndexOf(sNombres, "Ana", 3))
' introducir un valor a buscar en el array,
' si no existe se devuelve -1
Dim iPosicionBuscar As Integer
Console.WriteLine("Introducir nombre a buscar")
iPosicionBuscar = Array.IndexOf(sNombres, _
Console.ReadLine())
If iPosicionBuscar = -1 Then
Console.WriteLine("El nombre no está en el array")
Else
Console.WriteLine("El nombre está en la posición {0} del array", _
iPosicionBuscar)
End If
' buscar comenzando por la última posición
Dim iNumeros() As Integer
Dim iUltPosicionBuscar As Integer
iNumeros = New Integer() {10, 20, 30, 10, 50, 60, 10, 70, 80}
Console.WriteLine("El 10 está en la posición {0} comenzando por el final", _
Array.LastIndexOf(iNumeros, 10))
Console.ReadLine()
End Sub
Código fuente 348

Ordenación

Para ordenar un array disponemos del método Sort( ), que al estar sobrecargado, tiene varias implementaciones; la más básica de ellas es la que ordena la totalidad del array. También podemos ordenar una parte del array, indicando la posición inicial y cantidad de elementos a ordenar, etc. 
El método Reverse( ), invierte la posición de todos o parte de los elementos de un array. En este punto, debemos matizar que no se realiza un orden inverso de los elementos, sino que se cambian las posiciones de los mismos. Ver Código fuente 347.

Sub Main()
' ordenar todo el array
Dim sLetras1() As String = {"z", "a", "g", "m", "w", "i", "c", "b"}
Array.Sort(sLetras1)
Console.WriteLine("Ordenar todos el array")
MostrarArray(sLetras1)
' ordenar parte del array
Dim sLetras2() As String = {"z", "a", "g", "m", "w", "i", "c", "b"}
Array.Sort(sLetras2, 4, 3)
Console.WriteLine("Ordenar parte del array")
MostrarArray(sLetras2)
' invertir valores dentro del array
Dim sLetras3() As String = {"z", "a", "g", "m", "w", "i", "c", "b"}
Array.Reverse(sLetras3, 2, 4)
Console.WriteLine("Invertir valores del array")
MostrarArray(sLetras3)
Console.ReadLine()
End Sub
Private Sub MostrarArray(ByVal sMiLista() As String)
Dim iContador As Integer
For iContador = 0 To sMiLista.Length - 1
Console.WriteLine("Elemento: {0} - Valor: {1}", _
iContador, sMiLista(iContador))
Next
Console.WriteLine()
End Sub
Código fuente 347

Inicialización de valores

Para inicializar o eliminar los valores de los elementos de un array, utilizaremos el método Clear( ), al que pasaremos el array a inicializar, el índice a partir del que comenzaremos, y el número de elementos. 
Los valores serán inicializados en función del tipo de dato del array: cadena vacía en arrays String; cero en arrays numéricos, etc. Veamos el Código fuente 346.

Sub Main()
' array String, asignar valores e inicializar
Dim sLetras(2) As String
sLetras(0) = "a"
sLetras(1) = "b"
sLetras(2) = "c"
' limpiar elementos en un array de tipo String,
' los elementos limpiados quedan como cadena vacía
Array.Clear(sLetras, 0, 1)
Console.WriteLine("Array sLetras")
MostrarArray(sLetras)
' array Integer, asignar valores e inicializar
Dim iNumeros() As Integer = {100, 200, 300, 400, 500, 600}
' limpiar elementos en un array de tipo Integer,
' los elementos limpiados se ponen a 0
Array.Clear(iNumeros, 1, 2)
Console.WriteLine("Array iNumeros")
MostrarArrayNum(iNumeros)
' array Object, asignar valores e inicializar
Dim oVarios(6) As Object
oVarios(0) = "Hola"
oVarios(1) = 456
oVarios(2) = 1200
oVarios(3) = #12/25/2001#
oVarios(4) = 900
oVarios(5) = True
oVarios(6) = "adelante"
' al ser este un array de tipo Object
' los elementos limpiados se establecen a Nothing
Array.Clear(oVarios, 3, 2)
Console.WriteLine("Array oVarios")
MostrarArrayObj(oVarios)
Console.ReadLine()
End Sub
' recorrer un array de cadenas
Private Sub MostrarArray(ByVal sMiLista() As String)
Dim iContador As Integer
For iContador = 0 To sMiLista.Length - 1
Console.WriteLine("Elemento: {0} - Valor: {1}", _
iContador, sMiLista(iContador))
Next
Console.WriteLine()
End Sub
' recorrer un array de números
Private Sub MostrarArrayNum(ByVal iMiLista() As Integer)
Dim iContador As Integer
For iContador = 0 To iMiLista.Length - 1
Console.WriteLine("Elemento: {0} - Valor: {1}", _
iContador, iMiLista(iContador))
Next
Console.WriteLine()
End Sub
' recorrer un array de objetos
Private Sub MostrarArrayObj(ByVal oMiLista() As Object)
Dim iContador As Integer
For iContador = 0 To oMiLista.Length - 1
Console.WriteLine("Elemento: {0} - Valor: {1}", _
iContador, oMiLista(iContador))
Next
Console.WriteLine()
End Sub
Código fuente 346

Copia

Si intentamos copiar un array asignando la variable que contiene un array a otra, el resultado real serán dos variables que apuntan a la misma lista de valores, por lo que en definitiva sólo tendremos un array, al cual podremos acceder usando dos variables. Ello es debido a que como explicamos en un apartado anterior, los arrays son tipos por referencia que apuntan al mismo conjunto de valores. 
Podemos clonar el array, como se ha descrito en el apartado anterior, con lo que obtendremos un nuevo array, que será idéntico al original. O bien, podemos copiar el array utilizando los métodos CopyTo( ) y Copy( ) de la clase array. La diferencia con respecto a la clonación, consiste en que al copiar un array, el array destino ya debe estar creado con el número suficiente de elementos, puesto que los métodos de copia de la clase Array, lo que hacen es traspasar valores de los elementos del array origen al array destino, en función de los parámetros utilizados, copiaremos todos los elementos o un subconjunto. Veamos unos ejemplos en el Código fuente 345.

Sub Main()
Dim sColores(3) As String
sColores(0) = "Azul"
sColores(1) = "Verde"
sColores(2) = "Rosa"
sColores(3) = "Blanco"
MostrarArray(sColores)
' copiar usando el método CopyTo(),
' copiamos en el array sColorDestino,
' y comenzando por su posición 2, los
' valores del array sColores
Dim sColorDestino(6) As String
sColores.CopyTo(sColorDestino, 2)
Console.WriteLine("Array sColorDestino")
MostrarArray(sColorDestino)
' copiar usando el método Copy(),
' copiamos en el array sListaColores,
' a partir de su posición 2,
' 2 elementos del array sColores, comenzando
' desde la posición 1 de sColores
Dim sListaColores(5) As String
Array.Copy(sColores, 1, sListaColores, 2, 2)
Console.WriteLine("Array sListaColores")
MostrarArray(sListaColores)
Console.ReadLine()
End Sub
Private Sub MostrarArray(ByVal sMiLista() As String)
Dim iContador As Integer
For iContador = 0 To sMiLista.Length - 1
Console.WriteLine("Elemento: {0} - Valor: {1}", _
iContador, sMiLista(iContador))
Next
Console.WriteLine()
End Sub
Código fuente 345

Clonación

Para evitar el problema planteado en el apartado anterior, si necesitamos disponer de un array con las mismas características que uno ya existente, y que sea totalmente independiente del primero, utilizaremos el método Clone( ). 
Con esto solucionaremos el problema de que al pasar un array como parámetro, las modificaciones que precisemos realizar, afecten al array original. Veamos un ejemplo en el Código fuente 344.

Sub Main()
' crear un array
Dim iValores() As Integer = {10, 20, 30}
CambiaArray(iValores)
' mostrar el array original,
' en este no se habrán producido cambios
Console.WriteLine("Array original")
MostrarArray(iValores)
Console.ReadLine()
End Sub
Private Sub CambiaArray(ByVal iListaDatos As Integer())
' crear un array clónico,
' cambiarle valores y mostrarlo
Dim iListaClonada As Array
iListaClonada = iListaDatos.Clone()
iListaClonada(0) = 621
iListaClonada(1) = 900
Console.WriteLine("Array clónico")
MostrarArray(iListaClonada)
End Sub
Private Sub MostrarArray(ByVal sMiLista() As Integer)
Dim iContador As Integer
For iContador = 0 To sMiLista.Length - 1
Console.WriteLine("Elemento: {0} - Valor: {1}", _
iContador, sMiLista(iContador))
Next
Console.WriteLine()
End Sub
Código fuente 344

Paso de arrays como parámetros, y devolución desde funciones

Podemos pasar un array como parámetro a una rutina de código, teniendo en cuenta que los cambios que realicemos sobre el array en el procedimiento llamado, se mantendrán al volver el flujo de la ejecución al procedimiento llamador. 
Ello es debido a que los arrays son tipos por referencia del entorno, y por lo tanto, las variables del array que manejamos tanto desde el procedimiento llamador, como desde el procedimiento llamado, son en realidad punteros hacia una misma zona de memoria o referencia, la que contiene el array. En el ejemplo del Código fuente 343, comprobaremos que al pasar un array por valor, los cambios que realicemos sobre sus elementos se mantendrán al volver al procedimiento que hizo la llamada.

Sub Main()
Dim iValores() As Integer = {10, 20, 30}
' en ambos casos, se pasa una referencia del array,
' y al volver de las llamadas a los procedimientos,
' el array ha sido modificado en ambas llamadas,
' independientemente de que haya sido pasado por
' valor o referencia
ManipArrayVal(iValores)
MostrarArray(iValores)
ManipArrayRef(iValores)
MostrarArray(iValores)
Console.ReadLine()
End Sub
' a este procedimiento le pasamos un array por valor
Private Sub ManipArrayVal(ByVal iListaPorValor As Integer())
' cambiar elemento del array
iListaPorValor(0) = 888
End Sub
' a este procedimiento le pasamos un array por referencia
Private Sub ManipArrayRef(ByRef iListaPorReferencia As Integer())
' cambiar elemento del array
iListaPorReferencia(2) = 457
End Sub
Private Sub MostrarArray(ByVal sMiLista() As Integer)
' muestra el array pasado como parámetro
Dim iContador As Integer
For iContador = 0 To sMiLista.Length - 1
Console.WriteLine("Elemento: {0} - Valor: {1}", _
iContador, sMiLista(iContador))
Next
Console.WriteLine()
End Sub
Código fuente 343

Recorrer el contenido - II

La estructura de control utilizada para recorrer el array, puede ser indistintamente un bucle For...Next, For Each...Next, o la novedosa técnica de los objetos enumeradores proporcionados por el objeto array. 
Como muestra de estas funcionalidades, el Código fuente 342 que vemos a continuación, contiene algunos ejemplos de cómo realizar una iteración sobre los elementos de un array.

Sub Main()
' recorrer un array
' =================
Dim sNombres() As String = {"Ana", "Luis", "Pablo"}
Dim iContador As Integer
Dim sUnNombre As String
' modo tradicional
' ----------------
Console.WriteLine("Recorrido del array con LBound() y UBound()")
For iContador = LBound(sNombres) To UBound(sNombres)
Console.WriteLine("Posicion: {0} - Valor: {1}", _
iContador, sNombres(iContador))
Next
Console.WriteLine()
' con bucle For Each
Console.WriteLine("Recorrido del array con bucle For Each")
For Each sUnNombre In sNombres
Console.WriteLine("Nombre actual: {0}", sUnNombre)
Next
Console.WriteLine()
' modo orientado a objeto
' ----------------
' usando la propiedad Length
Console.WriteLine("Recorrido del array con propiedad Length")
For iContador = 0 To (sNombres.Length - 1)
Console.WriteLine("Posicion: {0} - Valor: {1}", _
iContador, sNombres(iContador))
Next
Console.WriteLine()
' usando los métodos GetLowerBound() y GetUpperBound()
Console.WriteLine("Recorrido del array con métodos GetLowerBound() y
GetUpperBound()")
For iContador = sNombres.GetLowerBound(0) To sNombres.GetUpperBound(0)
Console.WriteLine("Posicion: {0} - Valor: {1}", _
iContador, sNombres(iContador))
Next
Console.WriteLine()
' recorrer con un enumerador
Console.WriteLine("Recorrido del array con un enumerador")
Dim sLetras() As String = {"a", "b", "c", "d"}
Dim oEnumerador As System.Collections.IEnumerator
' obtener el enumerador del array
oEnumerador = sLetras.GetEnumerator()
' con un enumerador no es necesario posicionarse
' en el primer elemento ni calcular la cantidad
' de elementos del array, sólo hemos de avanzar
' posiciones con MoveNext() y obtener el valor
' actual con Current
While oEnumerador.MoveNext()
Console.WriteLine("Valor actual: {0}", oEnumerador.Current)
End While
Console.ReadLine()
End Sub
Código fuente 342

Recorrer el contenido - I

Para realizar un recorrido por los elementos de un array, disponemos de las funciones LBound( ) y UBound( ), que devuelven el número de índice inferior y superior respectivamente del array que pasemos como parámetro. Debido a que en .NET todos los arrays deben comenzar obligatoriamente por el índice cero, no será necesario el uso de LBound( ), ya que esta es una función que proviene de versiones anteriores del lenguaje, en las que el primer índice podía ser un número distinto de cero. 
La orientación a objetos proporcionada por el entorno, pone a nuestra disposición el conjunto de características que comentamos seguidamente, y que harán prácticamente innecesario el uso de las funciones de manejo de arrays, a favor de una codificación más orientada a objeto.
  • Length. Esta propiedad de un objeto array devuelve el número de elementos que contiene. 
  • GetLowerBound( ), GetUpperBound( ). Estos métodos de un objeto array, devuelven respectivamente, el número de índice inferior y superior de una dimensión del array. El resultado es el mismo que usando LBound( ) y UBound( ), pero desde una perspectiva orientada a objetos. 
  • Enumeradores. Un objeto enumerador pertenece al interfaz IEnumerator, diseñado para realizar un recorrido o iteración a través de uno de los diferentes tipos de colección (arrays incluidos) existentes en .NET Framework. Mediante el método GetEnumerator( ) de un objeto array, obtenemos un objeto que implementa el interfaz IEnumerator, que sólo puede realizar labores de lectura sobre el array, en ningún caso de modificación.

Asignación y obtención de valores

Para asignar u obtener valores de los elementos de un array, emplearemos la variable que contiene el array haciendo referencia al índice o posición a manipular, o bien, puesto que un array es un objeto, utilizaremos los métodos SetValue( ) y GetValue( ), que asignan y obtienen respectivamente los valores del array. Veamos un ejemplo en el Código fuente 341.

Sub Main()
' asignación de valores a los elementos de un array
' =================================================
Dim sNombres(4) As String
' directamente sobre la variable,
' haciendo referencia al índice
sNombres(0) = "Juan"
sNombres(1) = "Ana"
sNombres(2) = "Luis"
' o con el método SetValue(), asignando el
' valor en el primer parámetro y especificando
' la posición en el segundo
sNombres.SetValue("Elena", 3)
sNombres.SetValue("Miguel", 4)
' obtención de valores de un array
' ================================
Dim sValorA As String
Dim sValorB As String
sValorA = sNombres(2) ' directamente de la variable
sValorB = sNombres.GetValue(3) ' usando el meth GetValue
Console.WriteLine("Contenido de las variables")
Console.WriteLine("==========================")
Console.WriteLine("ValorA: {0} -- ValorB: {1}", sValorA, sValorB)
Console.ReadLine()
End Sub
Código fuente 341

Declaración

Podemos declarar un array en la forma tradicional, explicada en temas anteriores, o bien utilizando la sintaxis orientada a objetos mediante la palabra clave New. El Código fuente 340 muestra algunos ejemplos de las diferentes formas disponibles.

Sub Main()
' formas de declaración de arrays
' ===============================
' 1)
' estableciendo el número de elementos
Dim sNombres(2) As String
' 2)
' asignando valores al array al mismo tiempo que se declara,
' la lista de valores debe ir encerrada entre llaves
Dim sEstaciones() As String = {"Ana", "Pedro", "Luis"}
' 3)
' indicando el tipo de dato pero no el número de elementos,
' de este modo la variable todavía no es considerada un array
' ya que contiene una referencia a Nothing
Dim iValores() As Integer
' 4)
' indicando el tipo de dato y estableciendo una
' lista vacía de elementos,
' a diferencia del caso anterior, la variable ahora sí
' es considerada un array aunque de longitud cero
Dim iDatos() As Integer = {}
' 5)
' instanciando el tipo de dato, estableciendo el número
' de elementos al instanciar, e indicando que se trata de un array
' al situar las llaves
Dim iCantidades() As Integer = New Integer(20) {}
' 6)
' declarar primero la variable que contendrá el array,
' asignar valores al array al mismo tiempo que se instancia
' la lista de valores debe ir encerrada entre llaves
Dim iNumeros() As Integer
iNumeros = New Integer() {10, 20, 30, 10, 50, 60, 10, 70, 80}
End Sub
Código fuente 340

Recomendamos al lector, que en estos ejemplos con arrays, utilice el depurador para ejecutar línea a línea el código, y abra la ventana Locales también del depurador, para ver en cada caso el contenido de los elementos del array.

La clase Array

Esta clase, perteneciente a la jerarquía de clases del sistema, es decir, incluida en el espacio de nombres System, proporciona a través de sus miembros, acceso orientado a objeto para los arrays que manipulemos en nuestras aplicaciones. Esto quiere decir que los arrays, como sucede con otros elementos del lenguaje, son también objetos. 
Al igual que el resto de elementos del entorno, los arrays son tipos pertenecientes al sistema común de tipos de la plataforma o CTS, y se encuentran clasificados como tipos por referencia; esto quiere decir, que durante la ejecución, un array será gestionado en la zona de memoria conocida como montón o heap. 
Aunque podemos trabajar con los arrays como objetos, no será necesario instanciar un objeto de esta clase para poder disponer de un array. Al declarar una variable como array, implícitamente se instancia un objeto de la clase. En sucesivos apartados de este tema, haremos una descripción de los miembros de instancia y compartidos más importantes de la clase Array.

Arrays

Aspectos básicos 

Como ya se explicó en un tema anterior, un array es aquel elemento del lenguaje que nos permite agrupar un conjunto de valores del mismo tipo, y acceder a ellos a través de una misma variable o identificador, especificando la posición o índice en donde se encuentra el dato a recuperar. El Código fuente 339, muestra las operaciones esenciales que podemos realizar con un array. Recomendamos al lector la creación de un nuevo proyecto en el IDE de tipo consola, para realizar las pruebas mostradas en los siguientes apartados.

Sub Main()
' declarar un array de tipo String,
' el número de elementos es el indicado
' en la declaración más uno, porque la primera
' posición de un array es cero
Dim sNombres(3) As String
' asignar valores al array
sNombres(0) = "Ana"
sNombres(1) = "Pedro"
sNombres(2) = "Antonio"
sNombres(3) = "Laura"
' pasar un valor del array a una variable
Dim sValor As String
sValor = sNombres(2)
' mostrar en la consola el valor pasado a una variable
' y un valor directamente desde el array
Console.WriteLine("Valor de la variable sValor: {0}", sValor)
Console.WriteLine("Valor del array, posición 1: {0}", sNombres(1))
Console.ReadLine()
End Sub
Código fuente 339

Enlace dinámico de eventos - II

Como ventaja adicional, el objeto sobre el que vamos a manipular sus eventos podemos declararlo tanto a nivel local como en la zona de declaraciones, a diferencia del enlace estático, que nos obliga a declarar el objeto en la zona de declaraciones del módulo en el que vayamos a utilizarlo. Para establecer un enlace dinámico entre un evento y un manipulador, utilizaremos la instrucción AddHandler. Esta instrucción, recibe como primer parámetro el evento a conectar en el formato NombreObjeto.NombreEvento. Como segundo parámetro, pasaremos la dirección de entrada al procedimiento que deberá ejecutar el evento, y que obtenemos a través de la instrucción AddressOf. El Código fuente 338, muestra el procedimiento Main( ), en el que pedimos al usuario que introduzca un número, y según el valor obtenido, conectamos el evento con uno de los dos procedimientos manipuladores antes descritos.

Module Module1
'....
'....
Sub Main()
' pedir un número al usuario para conectar a uno de los
' dos procedimientos manipuladores de evento que hemos escrito
Dim liTipoManip As Integer
Console.WriteLine("Introduzca el número 1 ó 2," & _
" para seleccionar el manipulador de evento a utilizar")
liTipoManip = Console.ReadLine()
' instanciar un objeto Empleado
Dim loMiEmpleado As New Empleado()
' asignar un manejador de evento en tiempo de ejecución
' en función del número que el usuario ha introducido
Select Case liTipoManip
Case 1
AddHandler loMiEmpleado.LimiteSueldo, AddressOf SobreAsignacionSueldo
Case 2
AddHandler loMiEmpleado.LimiteSueldo, AddressOf SalarioIncorrecto
End Select
loMiEmpleado.Nombre = "ANTONIO"
' esta asignación provoca el evento,
' ello ejecutará uno de los manipuladores
' de evento que hemos conectado
loMiEmpleado.Sueldo = 2500
Console.ReadLine()
End Sub
'....
'....
End Module
Código fuente 338

Enlace dinámico de eventos - I

Siendo un poco más complejo a nivel sintáctico que el enlace estático, el enlace dinámico de eventos a sus correspondientes manipuladores, tiene la ventaja de que nos permite asociar el mismo evento a diferentes procedimientos manipuladores de dicho evento, durante el transcurso de la ejecución del programa. 
Por lo tanto, en el módulo de código donde tenemos a Main( ), vamos a escribir dos procedimientos que asociaremos dinámicamente al evento que hemos creado en la clase Empleado. Ver Código fuente 337

Module Module1
'....
'....
' manipuladores de evento que conectaremos en tiempo de ejecución
Public Sub SobreAsignacionSueldo(ByVal ldbImporte As Double)
Console.WriteLine("Se intentó asignar a un empleado el sueldo {0}" & _
ControlChars.CrLf & "¡ESTO ES INCORRECTO!", ldbImporte)
End Sub
Public Sub SalarioIncorrecto(ByVal ldbImporte As Double)
Console.WriteLine("INFORME DE INCIDENCIAS")
Console.WriteLine("======================")
Console.WriteLine("Error al intentar asignar el salario {0} a un empleado", _
ldbImporte)
End Sub
'....
'....
End Module
Código fuente 337

Enlace estático de eventos - II

Un pequeño truco que tenemos en el editor de código de VS.NET, para facilitar la creación de los procedimientos manipuladores de evento, consiste en abrir la lista Nombre de clase y seleccionar el nombre de la variable que hemos declarado WithEvents. Ver Figura 127.
Figura 127. Seleccionar objeto declarado WithEvents.
Seguidamente pasamos a la lista Nombre de método, y allí elegimos el nombre del evento que vamos a codificar. Ver Figura 128.
Figura 128. Seleccionar el evento a codificar.
Esto nos crea el procedimiento manipulador de evento vacío, en base a una convención de nombres predefinida en el IDE. Ver Código fuente 335.

Public Sub moEmple_LimiteSueldo(ByVal ldbImporte As Double) Handles
moEmple.LimiteSueldo
End Sub
Código fuente 335

Como hemos escrito el manipulador de evento para el objeto Empleado en un módulo, vamos ahora a escribir un procedimiento Main(), instanciando en el mismo, un objeto de esta clase. Asignaremos en primer lugar, un valor correcto a la propiedad Sueldo, y a continuación un valor que provocará el evento en la clase. Recomendamos al lector que ejecute el código línea a línea con el depurador, para observar el efecto cuando se produzca el evento.

Sub Main()
moEmple = New Empleado()
moEmple.Nombre = "Juan"
moEmple.Sueldo = 500 ' esta asignación no provoca el evento
moEmple.Sueldo = 8000 ' esta sí provoca el evento
End Sub
Código fuente 336

Enlace estático de eventos - I

Este es el modo mas sencillo para implementar la conexión entre un evento y un procedimiento manipulador de evento.
 En primer lugar, declaramos una variable del tipo de objeto cuyos eventos queremos capturar, en la zona de declaraciones del módulo, clase, etc., utilizando la palabra clave WithEvents. Veamos el Código fuente 332.

Module Module1
Private WithEvents moEmple As Empleado
'......
'......
Código fuente 332

A continuación, tenemos que escribir el procedimiento manipulador, que será invocado cada vez que se produzca el evento. Dicho procedimiento debe ser de tipo Sub, ya que un evento no puede devolver valores, por lo que no podremos utilizar un Function. También debemos finalizar su declaración con la palabra clave Handles, seguida del nombre de la variable del objeto que hemos declarado en la zona de declaraciones, y el nombre del evento que el procedimiento va a tratar. En el Código fuente 333, el procedimiento moEmple_LimiteSueldo( ), será llamado cada vez que se produzca el evento LimiteSueldo en el objeto Empleado.

Public Sub moEmple_LimiteSueldo(ByVal ldbImporte As Double) _
Handles moEmple.LimiteSueldo
Console.WriteLine("Se ha sobrepasado para {0} el límite" & _
" establecido de sueldo", _
moEmple.Nombre)
Console.WriteLine("El importe {0} no es válido", ldbImporte)
Console.ReadLine()
End Sub
Código fuente 333

El nombre utilizado para el procedimiento puede ser cualquiera, aunque en este caso hemos empleado la convención NombreObjeto_NombreEvento simplemente para facilitar la lectura del código, pero podríamos haber empleado, por ejemplo, el que se muestra en el Código fuente 334.

Public Sub Sobrepasado(ByVal ldbImporte As Double) _
Handles moEmple.LimiteSueldo
' ....
' ....
End Sub
Código fuente 334

Conexión de un emisor de eventos con un manipulador de eventos

Existen dos medios para comunicar un evento con un manipulador de eventos: 
  • En tiempo de compilación, realizando un enlace estático entre la clase y el manipulador mediante las palabras clave WithEvents y Handles. Esta técnica tiene la ventaja de que permite escribir un código mucho más legible, en cuanto a la manipulación de eventos se refiere. 
  • En tiempo de ejecución, realizando un enlace dinámico entre la clase y el manipulador mediante la palabra clave AddHandler. La ventaja en este caso, es que podemos asociar procedimientos manipuladores de evento dinámicamente durante el transcurso de la ejecución del programa.

El receptor de eventos

Un receptor de eventos, también denominado manipulador de eventos (event receiver o event handler), es aquella parte del código cliente, que configuramos para que sea capaz de recibir los eventos generados por un objeto emisor. Para que ambos elementos en este canal de comunicación que es la transmisión de eventos puedan operar, es necesario conectarlos.

El emisor de eventos

Un emisor de eventos, también denominado origen de eventos (event source o event sender), es un objeto capacitado para generar y lanzar eventos al sistema, que puedan ser recuperados por otros objetos preparados para realizar su tratamiento. Para que un objeto pueda desencadenar eventos, en su clase debemos realizar dos tareas: 
  • • Declarar el propio evento usando la palabra clave Event, especificando si es necesario una lista de parámetros que acompañan al evento. 
  • Lanzar el evento mediante la palabra clave RaiseEvent, seguida del nombre del evento a provocar. Si hemos declarado el evento con parámetros, deberemos añadir los valores para cada uno de los parámetros en el mismo orden en el que los hemos declarado. 

Situándonos pues ante el problema planteado por la clase Empleado en un apartado anterior, la solución que proponemos consistirá en generar desde la clase Empleado un evento cuando se produzca un fallo en la validación del sueldo. De esta manera, el código cliente que lo necesite, responderá al evento, y el que no lo precise, hará caso omiso del evento lanzado. 
En primer lugar, declaramos en la zona de declaraciones de la clase el evento LimiteSueldo, que irá acompañado de un parámetro que nos informará del importe erróneo que se intentaba asignar a la propiedad. 
A continuación, en la propiedad Sueldo, cuando detectemos que el sueldo sobrepasa el valor permitido, en lugar de lanzar allí el mensaje a la consola, generaremos el evento LimiteSueldo, que podrá ser recuperado por el código cliente que haga uso de la clase, actuando como necesite en cada ocasión. Observe el lector, que al mismo tiempo que lanzamos el evento, le pasamos el importe del sueldo que se intentaba asignar. Veamos el Código fuente 331.

Public Class Empleado
' declaramos el evento
Public Event LimiteSueldo(ByVal ldbImporte As Double)
Private msNombre As String
Private mdbSueldo As Double
Public Property Nombre() As String
Get
Return msNombre
End Get
Set(ByVal Value As String)
msNombre = Value
End Set
End Property
Public Property Sueldo() As Double
Get
Return mdbSueldo
End Get
Set(ByVal Value As Double)
' si el valor que intentamos asignar
' al sueldo supera el permitido...
If Value > 1000 Then
' ...lanzamos el evento, y le pasamos
' como parámetro informativo el valor
' incorrecto que intentábamos asignar
RaiseEvent LimiteSueldo(Value)
Else
mdbSueldo = Value
End If
End Set
End Property
End Class
Código fuente 331
Con estas modificaciones sobre la clase Empleado, ya tenemos listo nuestro emisor de eventos. Queda ahora por completar la parte que captura los eventos lanzados por el emisor.

Esquema básico de un sistema orientado a eventos

Un sistema conducido por eventos basa su funcionamiento en dos pilares fundamentales: un emisor y un receptor de eventos. El primero genera y lanza el evento al sistema, mientras que el segundo, si está interesado en tratar el evento lanzado, lo captura y le da respuesta. Si un objeto receptor no necesita gestionar eventos, simplemente no lo obtiene. Ver Figura 126.
Figura 126. Esquema de generación y captura de eventos.
Tras una introducción conceptual, a continuación trataremos con más detalle cada uno de los elementos integrantes de la gestión de eventos.

Programación basada en eventos

La aparición de sistemas operativos basados en ventajas trajo consigo un nuevo esquema en el desarrollo de aplicaciones. En un programa que se ejecute dentro de un sistema como Windows se están produciendo constantemente eventos (sucesos), provocados por las acciones del usuario o por el propio sistema. Tan elevado es el número de eventos que se producen, que dar respuesta a todos, es decir, codificar todas aquellas situaciones que acontecen a lo largo de la ejecución de un programa, es algo impensable. 
Por tal motivo, la técnica seguida al escribir código orientado a eventos se basa en codificar sólo los eventos que nos interese tratar, ya que para el resto, será el propio sistema quien proporcione el comportamiento por defecto. En una aplicación Windows típica, todos los elementos que forman parte de la misma, es decir, la propia ventana y los controles contenidos en ella, lanzan eventos en respuesta a las acciones del usuario. 
Un ejemplo típico: al pulsar un control de tipo botón en la ventana se produce su evento clic; si queremos que el programa realice alguna acción al pulsar dicho botón, deberemos escribir código en el procedimiento de evento asociado, para dar respuesta a tal suceso.

Un escenario de trabajo sin eventos

Supongamos que nos encargan desarrollar una clase llamada Empleado, entre cuyos miembros tenemos la propiedad Sueldo. Uno de los requerimientos respecto a esta propiedad es que su valor no debe ser superior a 1000; por ello, en su procedimiento Property, realizamos una validación a tal efecto, emitiendo un mensaje cuando el sueldo que asignemos sea superior. Ver el Código fuente 330.

Public Class Empleado
' variables de propiedad
Private msNombre As String
Private mdbSueldo As Double
' propiedad Nombre
Public Property Nombre() As String
Get
Return msNombre
End Get
Set(ByVal Value As String)
msNombre = Value
End Set
End Property
' propiedad Sueldo
Public Property Sueldo() As Double
Get
Return mdbSueldo
End Get
' al asignar un valor a la propiedad,
' si el valor es superior a 1000
' mostrar un mensaje y no permitir la
' asignación del sueldo
Set(ByVal Value As Double)
If Value > 1000 Then
Console.WriteLine("Asignación de sueldo incorrecta")
Console.ReadLine()
Else
mdbSueldo = Value
End If
End Set
End Property
End Class
Código fuente 330

Una vez finalizado el desarrollo de la clase, la distribuimos a nuestro cliente. Posteriormente, un nuevo cliente nos requiere la clase, pero en esta ocasión, aunque necesita la validación sobre la propiedad Sueldo, no quiere que se muestre el mensaje al sobrepasar el sueldo asignado. 
Se nos plantea en este caso un problema, ya que si escribimos una nueva versión de la clase Empleado, tendremos el trabajo extra de mantener ambas. Para solucionarlo mediante una única versión de la clase recurriremos a los eventos.

Programación estrictamente procedural

Antes de la llegada de los sistemas y lenguajes orientados a eventos, las aplicaciones ejecutaban su código en un orden fijo, ya que estaban basadas en un modelo construido exclusivamente a base de procedimientos: se realizaban llamadas a las rutinas de código en un orden predeterminado, y una vez terminada la ejecución de tales rutinas, finalizaba la aplicación.

Eventos en VB.NET

Ciñéndonos al ámbito de la programación, un evento es, dentro de un programa, una notificación lanzada por un objeto, que podrá ser contestada por aquellos otros objetos interesados en darle respuesta.

Eventos. ¿Qué es un evento?

Un evento es un suceso o situación, que acontece en una ubicación de espacio y tiempo no predecible. 
Cuando una máquina deja de funcionar por una avería, o cuando una persona resbala y cae, estamos en ambos casos, ante ejemplos de eventos, ya que ocurren en momentos inesperados. 
Para que se desencadene un evento, se deben dar determinadas circunstancias, las cuales favorecen el que dicho evento se produzca.

Formateo de valores - III

El Código fuente 328 muestra algunos formatos personalizados, construidos a base de patrones de formato.

Sub Main()
Dim ldtFecha As Date
ldtFecha = Date.Now()
Console.WriteLine(ldtFecha.ToString("ddd, dd-MMM/yyyy"))
Console.WriteLine(ldtFecha.ToString("dddd, a dd \de MMMM ,en el año yyyy"))
Console.WriteLine(ldtFecha.ToString("H:mm:s"))
End Sub
Código fuente 328

En cuanto a los números, si necesitamos aplicar un formato para este tipo de valor, podemos hacerlo mediante los caracteres mostrados en la Tabla 27.
El Código fuente 329 muestra algunos formatos aplicados sobre un tipo numérico.

Sub Main()
Dim ldcMiNum As Decimal
' crear un array con caracteres de formato
Dim lsFormatos() As String = {"c", "e", "f", "g", "n"}
Dim lsNumFormateado As String
ldcMiNum = 850.678 ' asignar valor al número
' recorrer el array de formatos y aplicar cada
' uno de los formatos al número
For Each lsNumFormateado In lsFormatos
Console.WriteLine(ldcMiNum.ToString(lsNumFormateado))
Next
Console.ReadLine()
End Sub
Código fuente 329

Formateo de valores - II

La Tabla 26 por otra parte, muestra algunos caracteres utilizados para crear patrones de formato personalizados, los cuales, se deben combinar entre sí, para componer el formato que necesitemos.
Tabla 26. Caracteres para patrones de formato

Formateo de valores - I

La utilización de un formato sobre un tipo de dato, nos permite mostrar su valor de un modo distinto a como se encuentra almacenado en la aplicación. Por ejemplo, el valor puro de una fecha no muestra el nombre del mes; sin embargo, aplicándole el formato adecuado, podemos hacer que se muestre la fecha en un modo extendido, con el nombre del mes, día de la semana, etc. Todos los tipos de datos del entorno que pueden mostrar información formateada, disponen del método ToString( ), al cuál podemos pasarle una cadena, con la especificación de formato que necesitemos. 
A continuación mostraremos unos ejemplos de formateo para fechas y números, ya que son los tipos de datos que con más frecuencia requieren ser formateado a lo largo del código de un programa.
Respecto a las fechas, el tipo Date, aparte del método ToString( ), tiene algunos miembros que devuelven un tipo de formato fijo. Veamos el Código fuente 326.

Sub Main()
Dim ldtFecha As Date
ldtFecha = Date.Now()
Console.WriteLine("ToLongDateString: {0}", ldtFecha.ToLongDateString())
Console.WriteLine("ToUniversalTime: {0}", ldtFecha.ToUniversalTime())
End Sub
Código fuente 326

Empleando alguna de las sobrecargas del método ToString( ), podemos formatear en los modos mostrados seguidamente. 
La Tabla 25 muestra algunos caracteres asociados a los formatos predefinidos.
En el Código fuente 327 podemos ver un formateo de fechas con caracteres de formato.
Sub Main()
Dim ldtFecha As Date
Dim lsListaFormatos() As String = {"d", "D", "g", "G", "t", "T", "m", "y"}
Dim lsFormato As String
ldtFecha = Date.Now()
For Each lsFormato In lsListaFormatos
Console.WriteLine("Formato: {0}, resultado: {1}", _
lsFormato, ldtFecha.ToString(lsFormato))
Next
End Sub
Código fuente 327

Operaciones aritméticas, la clase Math

La clase Math contiene el conjunto de operaciones aritméticas más habituales. Gracias a que sus miembros son compartidos, es muy fácil su uso, ya que sólo debemos especificar el nombre de la clase, seguido del método a ejecutar.

El Código fuente 325 muestra algunos ejemplos utilizando métodos de la clase Math. Consulte el lector la documentación de .NET Framework para una explicación detallada sobre todos los miembros de esta clase.

Sub Main()
Dim liSigno As Integer
Dim ldbRedondear As Double
' Abs(): devuelve el valor absoluto del número
' pasado como parámetro
Console.WriteLine("Abs --> {0}", Math.Abs(-1867.79))
' Ceiling(): devuelve el número sin precisión decimal,
' más grande o igual que el pasado como parámetro
Console.WriteLine("Ceiling --> {0}", Math.Ceiling(256.7235))
' Floor(): devuelve el número sin precisión decimal,
' más pequeño o igual que el pasado como parámetro
Console.WriteLine("Floor --> {0}", Math.Floor(256.7235))
' Sign(): devuelve un valor informando del signo del número
' pasado como parámetro
Console.WriteLine("Introducir número para averiguar su signo")
liSigno = Console.ReadLine()
Select Case Math.Sign(liSigno)
Case -1
Console.WriteLine("El número es negativo")
Case 0
Console.WriteLine("El número es cero")
Case 1
Console.WriteLine("El número es positivo")
End Select
' Round(): redondea el número pasado como parámetro
ldbRedondear = Math.Round(28.3215)
Console.WriteLine("Redondear 28.3215 --> {0}", ldbRedondear)
ldbRedondear = Math.Round(28.63215)
Console.WriteLine("Redondear 28.63215 --> {0}", ldbRedondear)
Console.ReadLine()
End Sub
Código fuente 325

El tipo Date (fecha)

Este tipo de dato, que utilizamos para trabajar con fechas, hace uso de la estructura DateTime, por lo que cuando tipificamos una variable como Date, los miembros que realmente manipulamos son los de un tipo DateTime. Consulte el lector, el apartado dedicado a la estructura DateTime para una mayor información.

La estructura Char - II

Para asignar un valor de manera explícita a una variable, parámetro, etc., de tipo Char, es recomendable situar el carácter c junto a dicho valor. Veamos el Código fuente 323.

Dim lcCaracter As Char
' ambas asignaciones son equivalentes, pero se recomienda la primera
lcCaracter = "H"c
lcCaracter = "H"
Código fuente 323

En el anterior ejemplo este aspecto es opcional, sin embargo, si queremos asignar un valor Char a una variable tipificada como Object, debemos utilizar irremisiblemente el indicador c junto al valor, o de otro modo, el subtipo almacenado en la variable Object lo tomará como String en lugar de Char. El mejor modo de comprobarlo, es abriendo la ventana Locales en modo de depuración. Veamos un ejemplo en el Código fuente 324.

Dim loValor As Object
loValor = "H" ' objeto de subtipo String
loValor = "H"c ' objeto de subtipo Char
Código fuente 324

La estructura Char - I

Mediante el uso de esta estructura podemos manejar tipos de datos simples de carácter. Los métodos compartidos de Char nos informarán del tipo de carácter que estamos manejando, además de poder realizar determinadas operaciones sobre dicho carácter. 
El Código fuente 322 muestra un ejemplo de uso de la estructura Char. Cada uno de los miembros de Char empleados se encuentra con un pequeño comentario aclaratorio de su funcionalidad.

Public Sub Main()
Dim lcCaracter As Char
Dim lsResultado As String
Dim lcConvertido As Char
Do
Console.WriteLine("Introducir un carácter o cero para salir")
lcCaracter = Convert.ToChar(Console.ReadLine())
lsResultado = ""
lcConvertido = Nothing
' IsDigit() indica si el carácter es un dígito decimal
If Char.IsDigit(lcCaracter) Then
lsResultado = "dígito"
End If
' IsLetter() indica si el carácter es una letra
If Char.IsLetter(lcCaracter) Then
lsResultado = "letra"
End If
' IsWhiteSpace() indica si el carácter es un espacio en blanco
If Char.IsWhiteSpace(lcCaracter) Then
lsResultado = "espacio"
End If
' IsPunctuation() indica si el carácter es un signo de puntuación
If Char.IsPunctuation(lcCaracter) Then
lsResultado &= "puntuación"
End If
' IsUpper() comprueba si el carácter está en mayúscula
If Char.IsUpper(lcCaracter) Then
lsResultado &= " mayúscula"
' ToLower() convierte el carácter a minúscula
lcConvertido = Char.ToLower(lcCaracter)
End If
' IsLower() comprueba si el carácter está en minúscula
If Char.IsLower(lcCaracter) Then
lsResultado &= " minúscula"
' ToUpper() convierte el carácter a mayúscula
lcConvertido = Char.ToUpper(lcCaracter)
End If
' mostramos una cadena con el tipo de carácter
Console.WriteLine("El carácter es: {0}", lsResultado)
' si hemos convertido el caracter a mayúscula/minúscula,
' lo mostramos
If Char.IsLetter(lcConvertido) Then
Console.WriteLine("El carácter se ha convertido: {0}", lcConvertido)
End If
Console.WriteLine()
' no salimos hasta que no se introduzca un 0
Loop Until lcCaracter = "0"c
End Sub
Código fuente 322

Conversión de tipos con la clase Convert

Esta clase nos permite convertir el contenido de una variable perteneciente a un tipo base del sistema a otro tipo base. Su uso es muy sencillo a través de los métodos compartidos que proporciona. 
El Código fuente 321 convierte un número a cadena, y después esa cadena a un número utilizando los métodos de esta clase.

Dim lsCadena As String
lsCadena = Convert.ToString(150) ' "150"
Dim liNum As Integer
liNum = Convert.ToInt32(lsCadena) ' 150
Código fuente 321

Manipulación de cadenas con la clase String - V

• Copy( ). Crea un nuevo objeto String, aunque el medio más sencillo consiste en asignar una cadena a la variable. Ver el Código fuente 318.

Dim lsCadA As String
Dim lsCadB As String
lsCadA = "uno"
lsCadB = String.Copy("OTRO")
Console.WriteLine("CadenaA --> {0}", lsCadA)
Console.WriteLine("CadenaB --> {0}", lsCadB)
Código fuente 318

• Compare( ). Este método compartido compara dos cadenas, y devuelve un valor menor de cero, si la primera cadena es menor que la segunda; cero si ambas cadenas son iguales; y mayor de cero, si la primera cadena es mayor. Ver el Código fuente 319.

Dim lsCompara1 As String
Dim lsCompara2 As String
Dim liResultaComp As Integer
Console.WriteLine("Introducir primera cadena a comparar")
lsCompara1 = Console.ReadLine()
Console.WriteLine("Introducir segunda cadena a comparar")
lsCompara2 = Console.ReadLine()
liResultaComp = String.Compare(lsCompara1, lsCompara2)
Select Case liResultaComp
Case Is < 0
Console.WriteLine("Primera cadena es menor")
Case 0
Console.WriteLine("Las cadenas son iguales")
Case Is > 0
Console.WriteLine("Primera cadena es mayor")
End Select
Código fuente 319

• Equals( ). Compara el objeto con una cadena pasada como parámetro, y devuelve un valor lógico, que indica si las cadenas son o no iguales. Ver el Código fuente 320.

Dim lsCadInicial As String
Dim lsCadComparar As String
lsCadInicial = "Prueba"
Console.WriteLine("Introducir una cadena a comparar con la cadena inicial")
lsCadComparar = Console.ReadLine()
If lsCadInicial.Equals(lsCadComparar) Then
Console.WriteLine("Las cadenas son iguales")
Else
Console.WriteLine("Las cadenas son diferentes")
End If
Código fuente 320

Manipulación de cadenas con la clase String - IV

• SubString( ). Obtiene una subcadena comenzando por una posición de la cadena, y extrayendo un número de caracteres. 
• IndexOf( ), LastIndexOf( ). Buscan una subcadena pasada como parámetro, comenzando por el principio y el fin respectivamente; y devuelven la posición de comienzo de dicha subcadena. Ver el Código fuente 315.

Dim lsCadCompleta As String
lsCadCompleta = "En el bosque se alza el castillo negro"
Console.WriteLine("Substring --> {0}", lsCadCompleta.Substring(6, 5)) '
"bosqu"
Console.WriteLine("IndexOf --> {0}", lsCadCompleta.IndexOf("el")) ' 3
Console.WriteLine("LastIndexOf --> {0}", lsCadCompleta.LastIndexOf("el")) '
21
Código fuente 315

• ToUpper( ), ToLower( ). Cambian la cadena a mayúsculas y minúsculas respectivamente. Ver el Código fuente 316.

Dim lsCadMayMin As String
lsCadMayMin = "CambIaNDO A mayúsCUlAs Y MINúscULAS"
Console.WriteLine("Pasar a may. --> {0}", lsCadMayMin.ToUpper())
Console.WriteLine("Pasar a min. --> {0}", lsCadMayMin.ToLower())
Código fuente 316

• Concat( ). Concatena dos cadenas pasadas como parámetro. Este es un método compartido de la clase String, por lo que no se requiere una instancia previa de la clase. El modo, sin embargo más rápido y sencillo para concatenar, sigue siendo el operador específico de concatenación: &. Ver el Código fuente 317.

Dim lsConcatenar As String
lsConcatenar = String.Concat("Hola ", "a todos")
lsConcatenar = "ahora usamos" & " el operador para concatenar"
Código fuente 317

Manipulación de cadenas con la clase String - III

• Insert( ). Inserta en la cadena, una subcadena a partir de una posición determinada. Ver el Código fuente 311.

Dim lsCadena As String
Dim lsAgregar As String
lsCadena = "Estamos programando"
lsAgregar = lsCadena.Insert(2, "HOLA") ' "EsHOLAtamos programando"
Código fuente 311.

• Remove( ). Elimina de la cadena un número determinado de caracteres, comenzando por una posición específica. Ver el Código fuente 312.

Dim lsCadena As String
Dim lsQuitar As String
lsCadena = "Estamos programando"
lsQuitar = lsCadena.Remove(5, 3) ' "Estamprogramando"
Código fuente 312

• Replace( ). Cambia dentro de la cadena, todas las ocurrencias de una subcadena por otra. Ver el Código fuente 313.

Dim lsCadCompleta As String
lsCadCompleta = "En el bosque se alza el castillo negro"
Console.WriteLine("Replace --> {0}", lsCadCompleta.Replace("el", "la"))
Código fuente 313

• StartsWith( ), EndsWith( ). Comprueban que en la cadena exista una subcadena al principio o final respectivamente. Ver el Código fuente 314.

Dim lsCadena As String
lsCadena = "veinte"
Console.WriteLine(lsCadena.StartsWith("ve")) ' True
Console.WriteLine(lsCadena.EndsWith("TE")) ' False
Código fuente 314

Manipulación de cadenas con la clase String - II

Debido al elevado número de miembros que contienen la mayoría de los tipos de la plataforma .NET, tanto clases, como estructuras, tipos de datos, etc.; y a que muchos de ellos disponen de versiones sobrecargadas; en la descripción de cada tipo haremos un repaso de sus miembros principales, remitiendo al lector a la documentación de referencia que sobre los tipos existe en la ayuda de la plataforma .NET, en donde encontrará toda la información detallada. 
Antes de comenzar a describir los métodos de esta clase, y puesto que una cadena es un array de tipos Char, es importante tener en cuenta que la primera posición corresponde al cero. Esta aclaración la realizamos fundamentalmente, de cara a los métodos que requieran el manejo de posiciones concretas de la cadena. 
• Trim( ), TrimStart( ), TrimEnd( ). Eliminan los espacios a ambos lados de una cadena, al comienzo o al final. Ver el Código fuente 309.

Dim lsCadena As String
lsCadena = " Hola .NET "
Dim lsQuitar As String
lsQuitar = lsCadena.TrimEnd() ' " Hola .NET"
lsQuitar = lsCadena.TrimStart() ' "Hola .NET "
lsQuitar = lsCadena.Trim() ' "Hola .NET"
Código fuente 309

• PadLeft( ), PadRight( ). Rellenan una cadena por la izquierda o derecha utilizando un determinado carácter de relleno. Debemos especificar la longitud total de la cadena resultante. Como el carácter de relleno es un tipo Char, podemos especificar que se trata de este tipo, situando junto al carácter de relleno, la letra c. Ver el Código fuente 310.

Dim lsCadena As String
Dim lsRellena As String
lsCadena = "Hola"
lsRellena = lsCadena.PadLeft(10) ' " Hola"
lsRellena = lsCadena.PadRight(10, "W"c) ' "HolaWWWWWW"
Código fuente 310

Manipulación de cadenas con la clase String - I

La clase String nos provee de un amplio abanico de métodos para realizar las más diversas operaciones. Observemos el Código fuente 307, y comparemos con el fuente del ejemplo anterior.

Dim lsCadena As String
Dim liLongitud As Integer
Dim lsCambiada As String
lsCadena = "esto es una prueba"
liLongitud = lsCadena.Length ' 18
lsCambiada = lsCadena.ToUpper() ' ESTO ES UNA PRUEBA
Código fuente 307

Al ser una cadena, tanto un tipo de dato como un objeto de la clase String, podemos manipularlo como cualquier otro objeto de la jerarquía de la plataforma. En esta ocasión, hemos recuperado la longitud de la cadena mediante su propiedad Length, y la hemos convertido a mayúsculas ejecutando su método ToUpper( ), asignando el resultado a otra variable. Para comprobar la versatilidad que ahora nos proporcionan los tipos de datos, cuando declaramos una variable String, podemos hacerlo en la forma tradicional de declaración, o al estilo OOP. Si consultamos la ayuda de .NET Framework, encontraremos una entrada con el título String Class, que describe este tipo como una clase más del sistema. Veamos el Código fuente 308.

Sub Main()
' modo tradicional
Dim lsCad1 As String
lsCad1 = "mesa"
' instanciar un objeto String y asignar valor
Dim loCad2 As New String("silla")
' declarar variable e instanciar un objeto
' String en la misma línea
Dim loCad3 As String = New String("coche")
' declarar variable e instanciar un objeto
' String en la misma línea; el constructor
' utilizado en este caso requiere un array
' de objetos Char; observe el lector la forma
' de crear un array, asignando valores al
' mismo tiempo
Dim loCad4 As String = New String(New Char() {"t", "r", "e", "n"})
Console.WriteLine("lsCad1 --> {0}", lsCad1)
Console.WriteLine("loCad2 --> {0}", loCad2)
Console.WriteLine("loCad3 --> {0}", loCad3)
Console.WriteLine("loCad4 --> {0}", loCad4)
Console.ReadLine()
End Sub
Código fuente 308

Una vez visto el fuente anterior, debemos realizar algunas aclaraciones. 
Podemos comprobar, utilizando el constructor de la clase String que recibe como parámetro un array Char, que el tipo String no pertenece puramente al conjunto de tipos primitivos de la plataforma, ya que internamente, el entorno manipula una cadena como un array de tipos Char; aunque para nuestra comodidad, este es un proceso transparente, que gestiona la plataforma por nosotros. 
En segundo lugar, y este también es un trabajo realizado transparentemente por el entorno, cada vez que creamos o instanciamos un tipo String, obtenemos lo que se denomina una cadena inalterable. Internamente, cuando realizamos una operación sobre la cadena: convertirla a mayúsculas, extraer una subcadena, etc., .NET crea una nueva instancia de String, asignándola a la misma variable. 
En apariencia realizamos modificaciones sobre la misma cadena, pero en realidad, cada operación genera nuevos objetos String. En este apartado realizaremos una revisión de los métodos de esta clase, a través de un conjunto de ejemplos, que a modo ilustrativo, nos permitan familiarizarnos con el modo en que se manejan cadenas en VB.NET.

Tipos de datos como objetos. Eventos

Los tipos de datos también son objetos 

En la mayoría de los lenguajes, Visual Basic incluido, cuando necesitemos un proceso que implique la manipulación de cadenas de caracteres, fechas, cálculos aritméticos, etc., podemos recurrir a un conjunto de funciones de soporte existentes en el lenguaje, para obtener, por ejemplo, la longitud de una cadena, subcadenas, conversiones, formatos, fecha actual, partes de una fecha, etc. El Código fuente 306, muestra cómo obtenemos la longitud de una cadena con la función Len( ).

Dim lsCadena As String
Dim liLongitud As Integer
lsCadena = "esto es una prueba"
liLongitud = Len(lsCadena) ' 18
Código fuente 306

A pesar de disponer de este conjunto de funciones de utilidad, en el caso particular de VB.NET veremos en los siguientes apartados que su uso no será necesario, aunque se proporcionan por compatibilidad con anteriores versiones de este lenguaje. 
Dentro de la plataforma .NET Framework, todos los elementos del lenguaje se consideran tipos: los propios tipos de datos, clases, estructuras, enumeraciones, etc., componen lo que se denomina el CTS, o sistema común de tipos, una enorme jerarquía que parte del tipo base Object, y del que heredan el resto de tipos de la plataforma. Por dicho motivo, utilizaremos el término tipo para referirnos tanto a clases, como estructuras, etc. 
Al ser los tipos de datos, uno de los muchos tipos existentes dentro del esquema del CTS, podemos manipularlos de la forma tradicional o como si fueran objetos; aspecto este, que trataremos en el siguiente apartado.

Enumeraciones - III

Para utilizar una enumeración definida en nuestra aplicación, debemos declarar una variable, a la que daremos como tipo de dato el mismo de la enumeración. Una vez creada, la forma de asignar un valor es muy sencilla, ya que en cuanto escribamos el operador de asignación, el editor de código nos abrirá una lista con los posibles valores que admite la variable, que corresponderán, evidentemente, sólo a los de la enumeración. De esta forma, facilitamos enormemente la escritura de código, ya que se reducen las posibilidades de error en la asignación de valores a la variable enumerada. Ver Figura 125.
Figura 125. Asignación de valor a una variable de tipo enumerado.
El valor almacenado en una variable de enumeración corresponderá al número de la constante que hayamos seleccionado. Al declarar la variable, su valor inicial será cero. 
No obstante, la manipulación de una enumeración va mucho más allá de la asignación y recuperación simple de las constantes que componen la enumeración. Cuando declaramos una variable de una enumeración, el contenido real de dicha variable es un objeto de la clase Enum; por lo tanto, podemos utilizar los métodos de dicho objeto, para realizar diversas operaciones. Para tareas genéricas, la clase Enum también dispone de métodos compartidos que podremos ejecutar directamente, sin necesidad de crear una enumeración previa. El Código fuente 305 muestra algunos ejemplos.
Module Module1
Public Enum Musicas As Integer
Rock
Blues
NewAge
Funky
End Enum
Sub Main()
' creamos una variable de enumeración
' y le asignamos valor
Dim lxMusic As Musicas
lxMusic = Musicas.NewAge
' el contenido de la variable es el número asignado
' a la constante
Console.WriteLine(lxMusic)
' el método ToString() permite mostrar el nombre
' de la constante elegida de la lista que tiene
' la enumeración
Console.WriteLine(lxMusic.ToString("G"))
' obtener los valores de las constantes de la enumeración
' con GetValues(), y los nombres con GetNames(); estos métodos
' son compartidos de la clase Enum, y reciben como parámetro una
' variable de enumeración de la que debe obtenerse el tipo ejecutando
' su método GetType(); devuelven un array con los datos pedidos
Dim liValores() As Integer
Dim lsNombres() As String
Dim liContador As Integer
liValores = System.Enum.GetValues(lxMusic.GetType())
lsNombres = System.Enum.GetNames(lxMusic.GetType())
Console.WriteLine()
Console.WriteLine("Valor - Nombre")
' recorrer los arrays y mostrar su contenido
For liContador = 0 To UBound(liValores)
Console.WriteLine(liValores(liContador) & Space(7) & _
lsNombres(liContador))
Next
' comprobar si un nombre introducido por el
' usuario está entre los nombres de las
' constantes en la enumeración
Dim lsNombreMusica As String
Console.WriteLine("Introducir nombre de constante")
lsNombreMusica = Console.ReadLine()
If System.Enum.IsDefined(lxMusic.GetType(), lsNombreMusica) Then
Console.WriteLine("El tipo de música sí está en la enumeración")
Else
Console.WriteLine("El tipo de música no está en la enumeración")
End If
Console.ReadLine()
End Sub
End Module
Código fuente 305