Una de las grandes novedades de la versión 2.0 de la plataforma .NET son sin duda los tipos genéricos. Esta nueva característica de los lenguajes Visual Basic .NET y C# y sus correspondientes compiladores, permite escribir código genérico que, a la hora de compilarse se transforma en código específico para un tipo de datos.

Ello permite crear código de métodos, estructuras, clases e incluso interfaces sin preocuparnos por los tipos de datos que vamos a utilizar a la hora de la verdad.

Hasta la fecha cuando queríamos escribir código genérico que pudiese trabajar indistintamente con dos o más tipos de datos no nos quedaba más remedio que utilizar el tipo 'object' que es la raíz de todas las clases. Con los nuevos Generics de .NET 2.0 ya no es necesario, y nuestro código puede disfrutar de la robustez de tipos concretos sin tener que comprometerse por adelantado con ninguno.

El ejemplo clásico de este concepto (lo usan casi todos los autores en los artículos que he visto) es el de la clase 'Pila' ('Stack' en inglés). La clase Stack se usa para almacenar objetos o tipos básicos de forma LIFO (Last In First Out), es decir, se almacenan elementos y luego se pueden recuperar empezando siempre por el último que se haya introducido. Independientemente del tipo de objetos que se almacenen en la pila el código para gestionarlos es el mismo. Si todos los objetos que se almacenarán son del mismo tipo es una ineficiencia utilizar el tipo object, siendo mejor usar tipos concretos. La solución clásica sería usar object o bien crear diversas versiones sobrecargadas de los métodos de la pila que tomasen los diferentes tipos de objeto que ésta puede manejar. Un rollo.

En C# 2.0, por ejemplo, la clase pila se definiría así:

public class Stack<CualquierTipo>
{
    private CualquierTipo[] m_items;
    public CualquierTipo Pop() {.....}
    public void Push(CualquierTipo data) {....}
}

Fíjate que si la clase la estuviéramos definiendo para, por ejemplo, números enteros, la definición sería la misma solo que en donde ahora hemos puesto 'CualquierTipo' pondría 'int'. Es la única diferencia ya que lo demás (el código que no hemos escrito), sería idéntico.

El fragmento anterior define una pila genérica. Para utilizarla debemos hacerla concreta, esto es, debemos crear una instancia de la misma diciendo qué tipo de datos queremos utilizar con ella. En código esto se traduce en que, por ejemplo, si queremos crear una pila de números enteros, tendríamos que escribir:

Stack<int> pila = new Stack(<int>);
pila.Push(5);
pila.Push(1);
int x = pila.Pop();

Como vemos se declara la pila indicando entre signos de menos y mayor el tipo que queremos usar para sustituir al genérico. Luego la podemos usar directamente como si siempre hubiera estado definida con ese tipo de datos. Es de lo más cómodo y flexible.

Por supuesto se puede usar cualquier tipo a la hora de definir una instancia concreta. Por ejemplo si tenemos una clase 'Usuario' y queremos gestionar una pila de usuarios sólo tenemos que crear una instancia específica así:

Stack<Usuario> pila = new Stack(<Usuario>);

También es posible utilizar varios tipos genéricos en una definición, sólo hay que separarlos con comas dentro de la pareja < y >.

Este artículo de Juval Lowy (en inglés) explica con todo lujo de detalles esta característica, creando varios ejemos interesantes. Lo recomiendo encarecidamente para aprender más. La sintaxis para Visual Basic difiere un poco pero el concepto es el mismo.

Escrito por un humano, no por una IA