20 agosto, 2007

Thread con argumentos

En un artículo anterior les compartía un ejemplo de cómo trabajar con aplicaciones multi-hilo, pero para la ejecución de métodos sin argumentos, ahora quiero darles un ejemplo de cómo, con algunas pequeñas variaciones, podemos compartir información entre hilos.

Esto lo hacemos gracias a al método ParameterizedThreadStart el cual permite pasar un argumento de tipo object mediante el método start del thread, pero es mejor si vemos el ejemplo:



Imports System.threading

Module Module1

    Sub DespliegaMensaje(ByVal _texto As Object)
        For i As Integer = 1 To 5
            Console.WriteLine("Mensaje {0}, generado por el hilo: {1}", _texto.ToString, Thread.CurrentThread.ManagedThreadId)
        Next
    End Sub

    Sub Main()
        Dim metodo_a_ejecutar As New ParameterizedThreadStart(AddressOf DespliegaMensaje)
        Dim hilos(5) As Thread
        For i As Integer = 1 To 5
            hilos(i) = New Thread(metodo_a_ejecutar)
            hilos(i).Start("Hilo N°:" + i.ToString)
            If i = 5 Then hilos(i).Priority = ThreadPriority.Highest
        Next
        Console.ReadKey()
        For i As Integer = 1 To 5
            hilos(i).Abort()
        Next
    End Sub

End Module


No he mencionado nada sobre el hilo.abort(), aunque si lo he usado en los ejemplos, este método es utilizado para terminar la ejecución del hilo.


En el siguiente enlace se puede encontrar más información sobre System.Threading

http://msdn.microsoft.com/es-es/library/system.threading(VS.80).aspx

21 julio, 2007

Threading

Todo desarrollador que se preocupe por el rendimiento de las aplicaciones que implementa debe conocer sobre el desarrollo con múltiples hijos (threads). El namespace system.threading contiene lo necesario para crear y administrar el multi-hilo de una aplicación.

Para crear un thread es necesario seguir los siguientes pasos:

  • Crear un método sin argumentos y que no retorne datos.
  • Crear un ThreadStart delegate que haga referencia al método creado en el paso anterior
  • Crear un objeto Thread que haga referencia al ThreadStart delegate anterior.
  • Invocar al thread.start para iniciar la ejecución del nuevo hilo.

Veamos un ejemplo de cómo crear un thread que despliega una línea de texto en la consola.

Imports System.threading

Module Module1

Sub DespliegaMensaje()

Console.WriteLine("Mensaje, generado por el hilo: {0}", Thread.CurrentThread.ManagedThreadId)

End Sub

Sub Main()

Dim metodo_a_ejecutar As New ThreadStart(AddressOf DespliegaMensaje)

Dim NuevoHilo As New Thread(metodo_a_ejecutar)

NuevoHilo.Start()

Console.ReadKey()

NuevoHilo.Abort()

End Sub

End Module

Ahora veamos un ejemplo con varios threads

Imports System.threading

Module Module1

Sub DespliegaMensaje()

For i As Integer = 1 To 5

Console.WriteLine("Mensaje, generado por el hilo: {0}", Thread.CurrentThread.ManagedThreadId)

Next

End Sub

Sub Main()

Dim metodo_a_ejecutar As New ThreadStart(AddressOf DespliegaMensaje)

Dim hilos(5) As Thread

For i As Integer = 1 To 5

hilos(i) = New Thread(metodo_a_ejecutar)

hilos(i).Start()

Next

Console.ReadKey()

For i As Integer = 1 To 5

hilos(i).Abort()

Next

End Sub

End Module

En algunas ocasiones es necesario esperar que alguno de los hilos finalice para proseguir con la ejecución de nuestro código, para esto utilizamos el método join:

Hilo.Join()

También podemos controlar la prioridad de cada hilo, esto lo hacemos con el método priority utilizando el enumerado ThreadPriority.

Highest

La más alta prioridad

AboveNormal

Alta prioridad

Normal

Prioridad normal

BelowNormal

Prioridad baja

Lowest

La más baja prioridad

14 julio, 2007

Manipulando imágenes

El dot net framework nos brinda algunas herramientas para realizar algunas labores comunes con imágenes. El namespace System.Drawing.Image nos brinda las utilidades necesarias para trabajar con BMP, JPG y TIF, realizando labores como agregarles texto, marcas de agua o cambiarles el tamaño.

Veamos cómo realizar algunas labores comunes,

Cargar una imagen previamente guardada en disco:

Dim Foto As Image = Image.FromFile("C:\Foto.bmp")

Crear una nueva imagen de 640 por 480 pixeles

Dim nueva_imagen As Bitmap = New Bitmap(640, 480)

En el siguiente ejemplo se crea una imagen nueva y se le agrega un rectángulo y un texto y luego se guarda en disco.

Imports System.drawing

Imports System.Drawing.Imaging

Module Module1

Sub Main()

Dim Nueva_imagen As New Bitmap(640, 480)

Dim Nuevo_Grafico As Graphics = Graphics.FromImage(Nueva_imagen)

Dim brocha As Brush = Brushes.AliceBlue

Dim letra As New Font("Arial", 12, FontStyle.Bold)

Dim formato As New StringFormat()

formato.Alignment = StringAlignment.Center

formato.LineAlignment = StringAlignment.Center

Nuevo_Grafico.DrawRectangle(Pens.White, 10, 10, 220, 100)

Nuevo_Grafico.DrawString("Creando Nueva Imagen", letra, Brushes.DarkGreen, 110, 50, formato)

Nueva_imagen.Save("c:\temp\prueba.jpg", ImageFormat.Jpeg)

End Sub

End Module

07 julio, 2007

Gráficos (Líneas, figuras geométricas)

En el namespace system.drawing podemos encontrar todo lo necesario para poder crear líneas y figuras geométricas que ayuden a mejorar la presentación de las aplicaciones que desarrollados, y lo mejor es que lo podemos hacer con unas pocas líneas de código.

Pero ¿cómo creamos la figura?, lo primero es crear un objeto del tipo graphics, luego creamos un pen, y luego llamamos el método de graphics según sea lo que vamos a dibujar ej. DrawPie, DrawRectangle, DrawLine, DrawEllipse, entre otros. Pero veamos un ejemplo concreto (se requiere una Windows aplication):

Dim figura As Drawing.Graphics

Dim lapiz As New Pen(Color.Chocolate, 5)

figura = Me.CreateGraphics

figura.DrawEllipse(lapiz, 80, 50, 30, 30)

figura.DrawEllipse(lapiz, 160, 50, 30, 30)

figura.DrawEllipse(lapiz, 10, 10, 250, 250)

figura.DrawLine(lapiz, 135, 100, 135, 140)

figura.DrawArc(lapiz, 90, 100, 90, 90, -180, -180)

También este namespace tiene otras utilidades para controlar la posición, tamaño, y el aspecto de los controles visuales. Veamos:

Empezando por lo más básico, una de las primeras cosas que tenemos que definir es la posición de la figura que deseamos dibujar, para esto usamos el sistema de coordenadas "x" y "y". Donde "x" es la distancia en pixeles desde el límite izquierdo y "y" es la distancia en pixeles desde el límite superior de nuestro contenedor, por ejemplo.

Control.Location = New Point(10, 10)

También es muy común definir el tamaño de la figura, para esto definimos el ancho y el alto de la siguiente forma.

Control.Size = New Size(30, 30)

Una vez definida la posición y el tamaño podemos definirle el color. Veamos,

Control. BackColor = Color.Blue

24 junio, 2007

Serialización Xml

Este formato es basado en XML y es recomendado cuando el objeto será des-serializado en diferentes plataformas o cuando va a ser trasportado por la red siempre y cuando no se requiera serializar miembros privados. Este formato brinda una gran interoperabilidad y es muy fácil de comprender.

'Persona.vb

Public Class Persona

Public Nombre As String

Public Apellido As String

Public Edad As Integer

Public Sub New()

MyBase.New()

End Sub

Public Sub New(ByVal _nombre As String, ByVal _apellido As String, ByVal _edad As String)

Nombre = _nombre

Apellido = _apellido

Edad = _edad

End Sub

End Class

'Serialize.vb

Imports System.IO

Imports System.Runtime.Serialization

Imports System.Xml.Serialization

Module Module1

Sub Main()

Dim Manuel As New Persona("Manuel", "Salas", 30)

Dim archivo As New FileStream("c:\temp\f_persona.xml", FileMode.Create)

Dim formateador As New XmlSerializer(GetType(Persona))

formateador.Serialize(archivo, Manuel)

archivo.Close()

End Sub

End Module

Como podemos observar es muy similar a las serializaciones anteriores, básicamente seguimos los mismos tres pasos:

  1. Instanciamos un FileStream para almacenar el objeto serializado.
  2. Instanciamos un Formateador para serializar el objeto.
  3. Serializamos el objeto llamando al método serialize del formateador.

Para des-serializar un objeto seguimos los mismos pasos de manera inversa:

  1. Instanciamos un FileStream para leer el objeto serializado.
  2. Instanciamos un Formateador para des-serializar el objeto.
  3. Des-serializamos el objeto llamando al método deserialize del formateador.

Veamos como des-serializar el objeto que serializamos en el fragmento de código anterior:

'Persona.vb

Public Class Persona

Public Nombre As String

Public Apellido As String

Public Edad As Integer

Public Sub New()

MyBase.New()

End Sub

Public Sub New(ByVal _nombre As String, ByVal _apellido As String, ByVal _edad As String)

Nombre = _nombre

Apellido = _apellido

Edad = _edad

End Sub

End Class

'Deserialize.vb

Imports System.IO

Imports System.Runtime.Serialization

Imports System.Xml.Serialization

Module Module1

Sub Main()

Dim Manuel As New Persona

Dim archivo As New FileStream("c:\temp\f_persona.xml", FileMode.Open)

Dim formateador As New XmlSerializer(GetType(Persona))

Manuel = formateador.Deserialize(archivo)

archivo.Close()

Console.WriteLine("Nombre ={0}, Apellido = {1}, Edad = {2}", Manuel.Nombre, Manuel.Apellido, Manuel.Edad)

Console.ReadKey()

End Sub

End Module

20 junio, 2007

Serialización SOAP

Esta serialización es basado en XML y es recomendado cuando el objeto será des-serializado en diferentes plataformas o cuando va a ser trasportado por la red. Los objetos serializados con este formato pueden ser tres o cuatro veces más grandes que los serializados mediante el formato binario. Serialicemos un objeto mediante el formato SOAP en un archivo para su posterior recuperación:

Imports System.IO

Imports System.Runtime.Serialization

Imports System.Runtime.Serialization.Formatters.Soap

Module Module1

<System.Serializable()> Public Class Persona

Public Nombre As String

Public Apellido As String

Public Edad As Integer

End Class

Sub Main()

Dim Manuel As New Persona()

Manuel.Apellido = "Salas"

Manuel.Nombre = "Manuel"

Manuel.Edad = 30

Dim archivo As New FileStream("c:\temp\f_persona.xml", FileMode.Create)

Dim formateador As New SoapFormatter

formateador.Serialize(archivo, Manuel)

archivo.Close()

End Sub

End Module

Como podemos observar básicamente son tres pasos los que debemos realizar para serializar un objeto:

  1. Instanciamos un FileStream para almacenar el objeto serializado.
  2. Instanciamos un Formateador para serializar el objeto (Es necesario agregar una referencia a "System.Runtime.Serialization.Formatters.Soap.dll").
  3. Serializamos el objeto llamando al método serialize del formateador.

Para des-serializar un objeto seguimos los mismos pasos de manera inversa:

  1. Instanciamos un FileStream para leer el objeto serializado.
  2. Instanciamos un Formateador para des-serializar el objeto.
  3. Des-serializamos el objeto llamando al método deserialize del formateador.

Veamos como des-serializar el objeto que serializamos en el fragmento de código anterior:

Imports System.IO

Imports System.Runtime.Serialization

Imports System.Runtime.Serialization.Formatters

Module Module1

<System.Serializable()> Public Class Persona

Public Nombre As String

Public Apellido As String

Public Edad As Integer

End Class

Sub Main()

Dim Manuel As New Persona()

Dim archivo As New FileStream("c:\temp\f_persona.xml", FileMode.Open)

Dim formateador As New Soap.SoapFormatter

Manuel = formateador.Deserialize(archivo)

archivo.Close()

Console.WriteLine("Nombre ={0}, Apellido = {1}, Edad = {2}", Manuel.Nombre, Manuel.Apellido, Manuel.Edad)

Console.ReadKey()

End Sub

End Module

14 junio, 2007

Serialización Binaria

La serialización binaria es muy eficiente, pero recomendado cuando el objeto serializado va a ser des-serializados solo en aplicaciones dot.net. Serialicemos un objeto en un archivo para su posterior recuperación:

Imports System.IO

Imports System.Runtime.Serialization

Imports System.Runtime.Serialization.Formatters

Module Module1

<System.Serializable()> Public Class Persona

Public Nombre As String

Public Apellido As String

Public Edad As Integer

End Class

Sub Main()

Dim Manuel As New Persona()

Manuel.Apellido = "Salas"

Manuel.Nombre = "Manuel"

Manuel.Edad = 30

Dim archivo As New FileStream("c:\temp\f_persona.dat", FileMode.Create)

Dim formateador As New Binary.BinaryFormatter

formateador.Serialize(archivo, Manuel)

archivo.Close()

End Sub

End Module

Como podemos observar básicamente son tres pasos los que debemos realizar para serializar un objeto:

  1. Instanciamos un FileStream para almacenar el objeto serializado.
  2. Instanciamos un Formateador para serializar el objeto.
  3. Serializamos el objeto llamando al método serialize del formateador.

Para des-serializar un objeto seguimos los mismos pasos de manera inversa:

  1. Instanciamos un FileStream para leer el objeto serializado.
  2. Instanciamos un Formateador para des-serializar el objeto.
  3. Des-serializamos el objeto llamando al método deserialize del formateador.

Veamos como des-serializar el objeto que serializamos en el fragmento de código anterior:

Imports System.IO

Imports System.Runtime.Serialization

Imports System.Runtime.Serialization.Formatters

Module Module1

<System.Serializable()> Public Class Persona

Public Nombre As String

Public Apellido As String

Public Edad As Integer

End Class

Sub Main()

Dim Manuel As New Persona()

Dim archivo As New FileStream("c:\temp\f_persona.dat", FileMode.Open)

Dim formateador As New Binary.BinaryFormatter

Manuel = formateador.Deserialize(archivo)

archivo.Close()

Console.WriteLine("Nombre ={0}, Apellido = {1}, Edad = {2}", Manuel.Nombre, Manuel.Apellido, Manuel.Edad)

Console.ReadKey()

End Sub

End Module

Como podemos observar en el ejemplo anterior, cuando creamos una clase personalizada y queremos serializarla debemos marcarla con el atributo <System.Serializable()>. También podemos seleccionar las propiedades que no queremos serializar, para esto utilizamos el atributo <System.NonSerialized()> antes del nombre de la propiedad que deseamos no serializar.

08 junio, 2007

Serialización

Serialización es el proceso mediante el cual tomamos un objeto y almacenamos su estado, de forma que lo podamos transportar o recuperar posteriormente. Esta tarea nos la facilita el framework mediante el namespace System.Runtime.Serialization.

Hay cuatro formatos diferentes para serializar un objeto:

Binaria:

Este formato es recomendado cuando el objeto serializado va a ser des-serializados solo en aplicaciones dot.net.

SOAP:

Este formato es basado en XML y es recomendado cuando el objeto será des-serializado en diferentes plataformas o cuando va a ser trasportado por la red. Los objetos serializados con este formato pueden ser tres o cuatro veces más grandes que los serializados mediante el formato binario.

XML:

Este formato es basado en XML y es recomendado cuando el objeto será des-serializado en diferentes plataformas o cuando va a ser trasportado por la red siempre y cuando no se requiera serializar miembros privados. Este formato brinda una gran interoperabilidad y es muy fácil de comprender.

Custom:

Aunque los anteriores formatos permiten una gran personalización, en algunos casos es necesario hacer una serialización totalmente personalizada, implementando la interface Iserializable.

02 junio, 2007

Colecciones basadas en llaves y valores (Diccionarios)

Siguiendo con el tema de las colecciones vamos a ver otro tipo de colecciones, las que almacenan pares de valores (una llave y un valor).

Hashtable

El Hashtable es una colección que guarda pares de valores, almacena una llave y un valor. Usualmente es usada cuendo se requiere obtener un valor basado alguna clave que conocemos. Por ejemplo, un nombre de usuario asociado a un nombre completo, o un código asociado a un proveedor, veamos el ejemplo:

Imports System.Collections

Module Module1

Sub Main()

Dim clientes As New Hashtable

clientes.Add("01", "Cliente_01")

clientes.Add("02", "Cliente_02")

clientes.Add("03", "Cliente_03")

clientes.Add("04", "Cliente_04")

For Each cliente As DictionaryEntry In clientes

Console.WriteLine("Código: {0}, Nombre: {1}", cliente.Key, cliente.Value)

Next

Console.ReadKey()

End Sub

End Module

Es importante que tomemos en cuenta que si tratamos de almacenar dos entradas con la misma llave, la primer entrada será sustituida, podemos decir más técnicamente que si el hash de las dos llaves es igual el valor será sustituido.

SortedList

Es prácticamente igual al hashtable con la diferencia que en esta colección los datos se almacenan de manera ordenada. Veamos:

Imports System.Collections

Module Module1

Sub Main()

Dim clientes As New SortedList

clientes.Add("03", "Cliente_03")

clientes.Add("02", "Cliente_02")

clientes.Add("04", "Cliente_04")

clientes.Add("01", "Cliente_01")

For Each cliente As DictionaryEntry In clientes

Console.WriteLine("Código: {0}, Nombre: {1}", cliente.Key, cliente.Value)

Next

Console.ReadKey()

End Sub

End Module

ListDiccionary

Es ListDiccionary es prácticamente igual al hashtable, solo que es más eficiente para colecciones pequeñas. Es considerada como una colección especializada, por eso la encontramos en System.Collections.Specialized. Veamos:

Imports System.Collections

Imports System.Collections.Specialized

Module Module1

Sub Main()

Dim clientes As New ListDictionary

clientes.Add("03", "Cliente_03")

clientes.Add("02", "Cliente_02")

clientes.Add("04", "Cliente_04")

clientes.Add("01", "Cliente_01")

For Each cliente As DictionaryEntry In clientes

Console.WriteLine("Código: {0}, Nombre: {1}", cliente.Key, cliente.Value)

Next

Console.ReadKey()

End Sub

End Module

HybridDictionary

Esta es otra colección especializada, vimos anteriormente que tenemos el hashtable, muy eficiente para colecciones grandes, y luego el ListDictionary para colecciones pequeñas, pero que pasa cuando la colección que estamos implementando es algunas veces grande y otras veces pequeña, para estos casos se recomiendo la implementación mediante el HybridDictionary, ya que esta colección tiene la capacidad de comportarse como un ListDictionary pero cuando la colección crece automáticamente se trasforma en un Hashtable, siendo entonces bastante eficiente para ambientes irregulares (donde el tamaño de las colección varía sustancialmente).

Imports System.Collections

Imports System.Collections.Specialized

Module Module1

Sub Main()

Dim clientes As New HybridDictionary

clientes.Add("03", "Cliente_03")

clientes.Add("02", "Cliente_02")

clientes.Add("04", "Cliente_04")

clientes.Add("01", "Cliente_01")

For Each cliente As DictionaryEntry In clientes

Console.WriteLine("Código: {0}, Nombre: {1}", cliente.Key, cliente.Value)

Next

Console.ReadKey()

End Sub

End Module

OrderedDictionary

El OrderedDictionary es otra colección especializada, y nos da prácticamente la misma funcionalidad de un hashtable, pero agrega la funcionalidad necesaria para poder acceder los ítems por medio de un índice. Adicional a los métodos del hashtable el OrderedDictionary tiene un insert y un removeat para poder controlar el orden de los elementos. Pero mejor veamos un ejemplo:

Imports System.Collections

Imports System.Collections.Specialized

Module Module1

Sub Main()

Dim clientes As New OrderedDictionary

clientes.Add("03", "Cliente_03")

clientes.Insert(0, "02", "Cliente_02")

clientes.Add("04", "Cliente_04")

clientes.Insert(0, "01", "Cliente_01")

For Each cliente As DictionaryEntry In clientes

Console.WriteLine("Código: {0}, Nombre: {1}", cliente.Key, cliente.Value)

Next

Console.ReadKey()

End Sub

End Module

30 mayo, 2007

Colecciones secuenciales de Objetos

Las colecciones Stack y Queue son dos tipos de colecciones que permiten el manejo de colecciones de datos secuenciales, veamos:

En el queue el primer elemento en entrar es el primero en salir (FIFO), veamos sus métodos

  • Enqueue = Agrega un elemento a la cola
  • Dequeue = Obtiene y elimina el siguiente método de la cola
  • Peek = Obtiene pero no elimina el siguiente método de la cola

Veamos un ejemplo:

Imports System.Collections

Module Module1

Sub Main()

Dim Cola As New Queue

Cola.Enqueue("Proceso_1")

Cola.Enqueue("Proceso_2")

Cola.Enqueue("Proceso_3")

Cola.Enqueue("Proceso_4")

Cola.Enqueue("Proceso_5")

While Cola.Count > 0

Console.WriteLine(Cola.Dequeue())

End While

Console.ReadKey()

End Sub

End Module

En el stack el último elemento en entrar es el primero en salir (LIFO), veamos sus métodos

  • Push = Agrega un elemento a la cola
  • Pop = Obtiene y elimina el siguiente método de la cola
  • Peek = Obtiene pero no elimina el siguiente método de la cola

Veamos un ejemplo:

Imports System.Collections

Module Module1

Sub Main()

Dim Pila As New Stack

Pila.Push("Proceso_1")

Pila.Push("Proceso_2")

Pila.Push("Proceso_3")

Pila.Push("Proceso_4")

Pila.Push("Proceso_5")

While Pila.Count > 0

Console.WriteLine(Pila.Pop())

End While

Console.ReadKey()

End Sub

End Module

Como podemos deducir de los ejemplos anteriores, el uso de ambas colecciones es bastante similar, la principal diferencia radica en el orden en el cual los elementos son extraídos.

26 mayo, 2007

Colecciones simples de Objetos

Vamos a conocer un poco sobre la colección más simple que nos proporciona el dot net Framework, el arraylist. Vemos los métodos más de mayor uso.

  • Add = Agrega un ítem a la colección
  • AddRange = Agrega una lista de ítems a la colección
  • Insert = Inserta un ítem en una lugar específico de la colección
  • InsertRange = inserta una lista de ítems en un lugar específico de la colección
  • Remove = Elimina un ítem de la colección
  • Remove at = Elimina un ítem de la colección basado en el índice del ítem
  • RemoveRange = Elimina una lista de ítems
  • Contains = Permite consultar si un ítem está contenido en la colección
  • Index of = Permite consultar el número del índice en el cual está contenido un ítem.
  • Clear = Elimina todos los ítem de la colección.

Antes de ver un ejemplo de cómo se usa todo esto, es importarte hacer referencia a que estas dos colecciones son colecciones de datos tipo object, por lo tanto, podríamos decir que son algo ineficientes desde el punto de vista de rendimiento, además, el compilador no nos ayuda mucho a prevenir errores de tipo de dato.

Imports System.Collections

Module Module1

Sub Main()

Dim datos As New ArrayList

For indice As Integer = 0 To 99

datos.Add(Rnd) 'Agrega un número aleatorio a la colección

Next

For indice As Integer = 99 To 0 Step -1

Console.WriteLine(datos(indice))

datos.RemoveAt(indice)

Next

Console.ReadKey()

End Sub

End Module

El recoger una colección de datos es muy tarea muy común y hay varias maneras de hacerlo, en el ejemplo anterior usamos un "For" pero el arraylist nos provee de la implementación del IEnumeration, esto nos permite recorrer la colección de una forma más fácil de entender y mantener, veamos:

Imports System.Collections

Module Module1

Sub Main()

Dim datos As New ArrayList

For indice As Integer = 0 To 99

datos.Add(Rnd) 'Agrega un número aleatorio a la colección

Next

Dim Navega_datos As IEnumerator = datos.GetEnumerator

Do While Navega_datos.MoveNext()

Console.WriteLine(Navega_datos.Current)

Loop

Navega_datos.Reset()

Console.ReadKey()

End Sub

End Module

Además de IEnumeration, contamos con la implementación de la interface ICollection, esta nos habilita el uso de lo siguiente:

  • Count = Retorna el número de elementos de la colección
  • Copyto = Copia el contenido de la colección en un arreglo

También es importante mencionar la interface Ilist que es la que le da la funcionalidad básica a la colección (add, addrange, clear, etc.)

24 mayo, 2007

Colecciones de datos

Todos los desarrolladores hemos tenido la necesidad de utilizar listas de datos, muchos solo hacen uso de los arreglos, sin embargo el dot net Framework nos proporciona una buena cantidad de listas especializadas, según el tipo de información que queramos manejar. Entonces el principal problema es determinar cuál es la lista más adecuada para el tipo de datos que queremos manejar. Conozcamos un poco las colecciones que encontramos en system.collections

Colección

Descripción

ArrayList

Almacena una lista de objetos

SortedList

Almacena una lista de objetos pero ordenados

Queue

Almacena una lista de objetos pero con la restricción de que el primer objeto en entrar es el primero en salir

Stack

Almacena una lista de objetos pero con la restricción de que el último objeto en entrar es el primero en salir

Hashtable

Almacena una colección de pares de valores, una llave y un valor.

SortedList

Almacena una colección de pares de valores, una llave y un valor, pero a diferencia de hashtable, los almacena de manera ordenada.

ListDiccionary

Tiene la misma funcionalidad del Hashtable, solo que optimizado para colecciones pequeñas (con pocos elementos)

HybridDiccionary

Esta colección se comporta como un ListDictionary cuando tiene pocos elementos, pero si la colección crece, toma el comportamiento del hashtable.

OrderedDictionary

Es muy similar al hashtable, pero agrega la funcionalidad necesaria para poder acceder a los elementos mediante un índice.

StringCollection

Es muy similar al ArrayList pero para manejar colecciones de string exclusivamente.

StringDiccionary

Es muy similar al Hashtable pero para manejar colecciones de string, tanto la llave como el valor tienen que ser string.

Las colecciones anteriormente citadas (a excepción de StringCollection y StringDiccionary) usan object como elementos, esto permite una gran confiabilidad pero no es lo mejor tomando en cuenta confiabilidad y el rendimiento. Cuando requiere de mejor rendimiento o mayor estabilidad de la aplicación (mediante la validación de tipos), el framework nos proporciona una serie de colecciones equivalentes a las anteriormente citadas, estas son llamadas colecciones genéricas (generic collection), y tienen la particularidad de comportarse igual que las anteriores pero con la particularidad que podemos definirle el tipo de dato que vamos a almacenar, ya no nos vemos obligados a usar el tipo "object".

El funcionamiento de estas colecciones es tan similar a las anteriormente vistas que existe una tabla de equivalencias, en la siguiente tabla podemos ver a la izquierda las colecciones que ya conocemos, pero cuando necesitemos trabajar con colecciones de un tipo de dato definido debemos usar el equivalente de la derecha, veamos:

Colección

Colección genérica

ArrayList

List

Queue

Queue

Stack

Stack

Hashtable

Dictionary

SortedList

SortedList

ListDictionary

Dictionary

HybridDictionary

Dictionary

OrderedDictionary

Dictionary

SortedDictionary

SortedDictionary

NameValueCollection

Dictionary

DictionaryEntry

KeyValuePair

StringCollection

List

StringDictionary

Dictionary

En futuros post espero poder mostrarles el funcionamiento de la mayoría de las colecciones.

22 mayo, 2007

Validando strings mediante patrones

En los sistemas de información la frase "Basura entra basura sale" es muy conocida, pero esta frase nunca nos debe servir para justificar las salidas incorrectas de un sistema, nos debe servir como experiencia que nos lleve a validar de mejor manera la información digitada por el usuario.

El Dot. Net Framework nos proporciona una serie de clases contenidas en el namespace system.text.regularexpressions que nos permiten validar la información del usuario mediante patrones conocidos como regular expression. Sinceramente debo indicar que la creación de estos patrones es bastante complicada y difícil de darle mantenimiento.

Para poder validar un string debemos primero crear un patrón, este patrón consta de una serie de caracteres que tienen un significado especial, veamos algunos de estos caracteres especiales y su significado.

Carácter

Uso

^

Inicio del string

$

Fin del string

\d{}

Solo dígitos numéricos, la cantidad la determina el número colocado entre {}

[ ]

Valida que el dígito sea igual a cualquiera de los caracteres contenidos entre las llaves

[ - ]

Valida que el dígito sea igual a cualquiera de los caracteres contenidos entre las llaves

*

Implica que repite el carácter precedente o puede no existir carácter

+

Implica que repite el carácter precedente estrictamente (debe de existir)

\s

Implica un espacio en blanco, puede ser espacio o tabulación

Veamos algunos ejemplos:

Para validar que se digite un teléfono válido, con el siguiente formato ###-#### podríamos usar el siguiente patrón:

Dato a Validar

Formato

Patrón

Número telefónico

###-####

^\d{3}-\d{4}$

Cédula (con guiones)

#-###-###

^[1-9]-\d{3}-\d{3}$

Correo Electrónico

nombre@dominio

^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)(([\w-]+\.)+))([a-zA-Z]{2,4}[0-9]{1,3})(\]?)$

Veamos un fragmento de código que nos aclare cómo utilizar regular expression

Imports System.Text.RegularExpressions

Module Module1

Sub Main()

Dim Mascara As String = ""

Dim PalabraaEvaluar As String = ""

While PalabraaEvaluar <> "Salir"

Console.Write("Máscara: ")

Mascara = Console.ReadLine()

Console.Write("Palabra: ")

PalabraaEvaluar = Console.ReadLine()

If Regex.IsMatch(PalabraaEvaluar, Mascara) Then

Console.WriteLine("Pasa la evaluación")

Else

Console.WriteLine("NO Pasa la evaluación")

End If

Console.ReadKey()

End While

End Sub

End Module

También podemos usar regular expressions para extraer fragmentos de un string, utilizando patrones similares a los anteriormente vistos

Imports System.Text.RegularExpressions

Module Module1

Sub Main()

Dim Completo As String = "Nombre: Alejandro Soto"

Dim Extracto As Match

Extracto = Regex.Match(Completo, "Nombre: (.*$)")

Console.WriteLine(Extracto.Groups(1))

Console.ReadKey()

End Sub

End Module

Hay libros completes sobre regular expression, el sentido de este post es solo hacer una breve reseña que sirva de introducción al tema.

19 mayo, 2007

Almacenamiento aislado (isolated storage)

En nuestros días se ha proliferado gran cantidad de malware (virus, spyware, adware, gusanos, troyanos, bots y otros). Estoy convencido que una de las causas de esta proliferación es la falta de seguridad en las aplicaciones que desarrollamos, prácticamente invitamos a los desarrolladores de malware a usar nuestra aplicaciones como entrada a las computadoras.

Cuando nuestra aplicación tiene que guardar archivos de configuración, preferencias o datos temporales, se nos presenta el problema de decidir donde guardarlo, ya que depende de donde lo guardemos nuestra aplicación dependerá de los privilegios del usuario que lo ejecute. Por eso creo que vale la pena escribir sobre la solución propuesta en el .Net Framework.

La Clase IsolatedStorageFile:

Esta clase nos provee de la funcionalidad necesaria para crear archivos y carpetas en las cuales nuestra aplicación pueda crear y accesar archivos en un almacenamiento seguro, aislado e independiente de los privilegios del usuario que ejecuta la aplicación. Tenemos dos tipos de almacenamiento:

Assembly/Machine: Permite crear un espacio de almacenamiento para la aplicación, independiente del usuario.

Assembly/User: Permite crear un espacio de almacenamiento independiente para el usuario.

Veamos cómo se usa:

Imports System.io

Imports system.IO.IsolatedStorage

Module Module1

Sub CreaArchivoenIsolated()

Dim EspacioenUsuario As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()

'Creamos un directorio llamado Usuario1 (opcional)

EspacioenUsuario.CreateDirectory("Usuario1")

Dim ArchivoUsuario As IsolatedStorageFileStream = New IsolatedStorageFileStream("Usuario1\user.cfg", IO.FileMode.Create, EspacioenUsuario)

Dim UsuarioStream As StreamWriter = New StreamWriter(ArchivoUsuario)

'Se escribe en el archivo lo que necesitemos almacenar

UsuarioStream.WriteLine("Personalización del Usuario")

UsuarioStream.WriteLine("X = 10")

UsuarioStream.WriteLine("Y = 50")

UsuarioStream.Close()

ArchivoUsuario.Close()

EspacioenUsuario.Close()

End Sub

Sub EjemploDirectorio()

'Podemos listar los directorios creados en el Espacio de Usuario

Dim EspacioenUsuario As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()

'Creamos otro directorio, solo para ejemplo.

EspacioenUsuario.CreateDirectory("Usuario2")

Dim Directorios() As String = EspacioenUsuario.GetDirectoryNames("*")

For Each directorio As String In Directorios

Console.WriteLine("Directorio encontrado: {0}", directorio)

Next

End Sub

Sub LeeArchivoenIsolated()

Dim EspacioenUsuario As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()

Dim ArchivoUsuario As IsolatedStorageFileStream = New IsolatedStorageFileStream("Usuario1\user.cfg", IO.FileMode.Open, EspacioenUsuario)

Dim UsuarioStream As StreamReader = New StreamReader(ArchivoUsuario)

Console.WriteLine(UsuarioStream.ReadToEnd)

Console.ReadKey()

End Sub

Sub Main()

CreaArchivoenIsolated()

EjemploDirectorio()

LeeArchivoenIsolated()

End Sub

End Module

Consideraciones importantes

  1. En el isolatedstorage no podemos usar File.exists, por esto es necesario usar el GetFileNames("nombre_a_buscar") esto nos retorna un arreglo de string, en este debemos consultar por el length, si este es igual a 0 significa que el archivo consultado no existe.
  2. Para estar seguros que una clase puede accesar el isolatedstorage es necesario demandar el permiso anteponiendo la siguiente instrucción a nuestra clase <IsolatedStorageFilePermission(SecurityAction.Demand)>