viernes, 9 de mayo de 2014

Receta No. 2-12 en C#: Creación de un Tipo de Dato Genérico

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 ¿Cómo declarar un tipo genérico?
3.2 Ejemplo de lista genérica
3.3 Consideraciones adicionales acerca del uso de genéricos
3.4 Uso de la clausula where
4. Práctica: Código Fuente C#
5. Conclusiones
6. Glosario
7. Enlaces & Literatura

0. Introducción

En la receta previa -Uso de una Colección con Tipos de Datos Específicos- vimos que es posible crear instancias de una estructura de datos (e.g., List<T>, Queue<T>, Dictionary<TKey, TValue>, Stack<T>) especificando un tipo de dato concreto a almacenar dentro la estructura. Ahora que ya tenemos una idea básica o fundamental de genéricos en C#, vamos a desarrollar esta receta enfocada a la creación de un tipo genérico (clase, estructura, o interfaz). ¡Manos a la obra!

1. Problema

Una de las soluciones software de nuestra aplicación, a pesar de que funciona correctamente, merece una actualización. El departamento de desarrollo ha encargado a la división de soluciones informáticas para PYMES, hacer un mantenimiento y actualización a esta aplicación y adaptarla a los cambios surgidos a partir de la versión 2.0 del Framework .NET de Microsoft.

2. Solución

A partir de la versión 2.0 del Framework .NET ya es posible programar tipos genéricos en el lenguaje de programación C#. A partir de la definición tipos genéricos podemos aprovechar para actualizar (refactorizar) varios de los tipos de la aplicación para mejorar su desempeño: mayor consistencia y con menor riesgo de generación de errores en el tratamiento de tipos en estructuras de datos o tipos genéricos.

3. Discusión de la Solución

Ya en la receta anterior vimos que varias de las estructuras de datos en el namespace System.Collections.Generic (e.g., List<T>Queue<T>Dictionary<TKey, TValue>Stack<T>). Esta característica brinda al programador cierta rigurosidad en la manipulación de los tipos a almacenar en la estructura. Este concepto de tratamiento es conocido (en inglés) como: strongly typed.

El concepto de strongly typed consiste en el tratamiento (agregación, remoción, actualización, lectura) seguro.

3.1 ¿Cómo declarar un tipo genérico?

Procedamos a conocer la sintaxis en C# para crear tipos genéricos. Empecemos por la definición de una clase:

public class TipoGenerico<T [,{...}]>

(La notación [,{...}] para señalar que TipoGenerico puede contener uno más parámetros de tipo)

Al parámetro de tipo T también lo podríamos llamar parámetro génerico de tipoTracionalmente se ha utilizado la letra T para referirnos a un parámetro de tipo, y esa tradición empezó con la introducción de las plantillas (templates [4]) en el lenguaje de programación C++.

Ahora echemos un vistazo a la especificación de una de las estructuras de datos definidas en System.Collections.Generic:

Clase IDictionary<TKey, TValue> [5]:

public class Dictionary : IDictionary, {...}

Se hace evidente el uso múltiple de parámetro de tipo. En este caso tenemos dos: TKey, y TValue.

3.2 Ejemplo de lista genérica

Veamos este breve ejemplo de creación de una clase -ListaGenerica<T>- para una lista genérica:

public class ListaGenerica<T>
{
void Agregar(T elemento)
{
// Implementación
}
}

public class PruebaListaGenerica
{
public static void Main()
{
// Creación de lista genérica con tipos string:
ListaGenerica<string> listaCadenas = new ListaGenerica<string>();
// Creación de lista genérica con tipos double:
ListaGenerica<double> listaDouble = new ListaGenerica<double>();
// Creación de lista genérica con tipos Button:
ListaGenerica<Button> listaBotones = new ListaGenerica<Button>();
}
}

En el código cliente de la clase PruebaListaGenerica podemos observar que nuestra lista genérica admite diferente tipos de datos (en particular: stringdouble, y Button). En tiempo de ejecución, el tratamiento de la lista estará acotado a tratar sólo con ese tipo de dato particular, cualquier intento de agregar un tipo no admisible, generará la excepción System.InvalidCastException [4]. Debo aclarar que la CLR (Common Language Runtime), en tiempo de ejecución (valga el pleonasmo), tratará de convertir implícitamente el tipo pasado como parámetro de tipo.

3.3 Consideraciones adicionales acerca del uso de genéricos

Vale tener en cuenta las especificaciones acerca del uso de genéricos aportada por [1]:
  • El parámetro de tipo puede ser un tipo por valor o un tipo referencia.
  • Los tipos por referencia deben poseer un constructor con cero argumentos (vacío) para ascender el parámetro de tipo para el tipo.
  • Se permite la jerarquía de herencia y de implementación para el parámetro de tipo.

3.4 Uso de la clausula where

Con la clausula where podemos establecer una restricción de jerarquía sobre el parámetro de tipo. Así:

public class NuevoTipoGenerico<T> where T : {Clase | Interfaz}


T puede heredar (desde Clase) o implementar (a Interfaz). Los argumentos que se pasen al tipo genérico debe pertenecer o implementar a la jerarquía de herencia o de implementación.

4. Práctica: Código Fuente C#

El siguiente ejemplo consistirá en un tipo genérico representa a una bolsa que puede contener n cantidad de elementos (se admiten duplicados) sin orden alguno (desordenados), y éstos independientes del tipo.

Archivo Bolsa.cs:

[Nota: Omito explicación de código fuente debido a que éste posee documentación.]

Compilación:


  1. csc /target:exe Bolsa.cs

> Prueba de ejecución.

Resultado (puede variar dado que el contenido de la bolsa se extrae de forma aleatoria):

Contenido de la bolsa:
 Elemento: DotNET
 Elemento: Microsoft
 Elemento: C#
 Elemento: Language
 Elemento: CLR
 Elemento: Framework

Extracción de elementos:
 Elemento removido: Framework
 Elemento removido: Microsoft
 Elemento removido: C#

Contenido actual de la bolsa:
 Elemento: DotNET
 Elemento: Language
 Elemento: CLR

Removiendo todos los elementos de la bolsa:

Contenido actual de la bolsa:

5. Conclusiones

Vimos a través de recete varios de los conceptos de tipos genéricos. Creamos un tipo genérico (Bolsa) para demostrar varias operaciones comunes de los tipos de esta naturaleza. Vale decir una vez más que, los genéricos simplifican el desarrollo y recude los errores que pudieran ocurrir al tratar todos los elementos de una colección o arreglo como System.Object.

6. Glosario

  • Actualización
  • Aplicación
  • CLR
  • Colección
  • Collection
  • Genéric
  • Mantenimiento
  • Object
  • Refactorización

7. Enlaces & Literatura

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: Generics (C# Programming Guide) - http://msdn.microsoft.com/en-us/library/512aeb7t.aspx
[3]: Template (C++), the free encyclopedia - https://en.wikipedia.org/wiki/Template_%28C%2B%2B%29
[4]: ArgumentException Class (System) - http://msdn.microsoft.com/en-us/library/system.argumentexception%28v=vs.110%29.aspx
[5]: Dictionary(TKey, TValue) Class (System.Collections.Generic) - http://msdn.microsoft.com/en-us/library/xfhwa508%28v=vs.110%29.aspx
[6]: where clause (C# Reference) - http://msdn.microsoft.com/en-us/library/bb311043.aspx


M

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.