Share via


Clases genéricas (Guía de programación de C#)

Actualización: noviembre 2007

Las clases genéricas encapsulan operaciones que no son específicas de un tipo de datos concreto. Las clases genéricas se utilizan frecuentemente con colecciones como listas vinculadas, tablas hash, pilas, colas, árboles, etc. Las operaciones de agregar y quitar elementos de la colección se realizan básicamente de la misma forma, independientemente del tipo de datos que se van a almacenar.

En la mayoría de los escenarios que necesitan clases de colección, el enfoque recomendado es utilizar las que proporciona la biblioteca de clases de .NET Framework. Para obtener más información sobre el uso de estas clases, vea Tipos genéricos en la biblioteca de clases de .NET Framework (Guía de programación de C#).

Normalmente, para crear clases genéricas se empieza a partir de una clase concreta existente y se cambian los tipos, uno a uno, por parámetros de tipo hasta que se obtiene un equilibrio óptimo entre generalización y utilidad. Al crear sus propias clases genéricas, debe tener en cuenta las siguientes consideraciones importantes:

  • Tipos que se generalizan como parámetros de tipo.

    Como regla general, cuantos más tipos se puedan parametrizar, más flexible y reutilizable será el código. Sin embargo, un exceso de generalización puede producir código difícil de leer o comprender para otros programadores.

  • Restricciones, en su caso, que se aplicarán a los parámetros de tipo (Vea Restricciones de tipos de parámetros (Guía de programación de C#)).

    Una buena regla consiste en aplicar el mayor número de restricciones posible que siga permitiendo manejar los tipos que se deben utilizar. Por ejemplo, si sabe que la clase genérica está exclusivamente destinada al uso con tipos de referencia, aplique la restricción de clase. Esto evitará el uso imprevisto de la clase con tipos de valor y permitirá utilizar el operador as en T y comprobar si hay valores nulos.

  • Factorizar o no el comportamiento genérico en las clases base y las subclases.

    Dado que las clases genéricas pueden actuar como clases base, se aplican las mismas consideraciones de diseño que con las clases no genéricas. Vea las reglas sobre cómo heredar de las clases base genéricas más adelante en este tema.

  • Implementar o no una o varias interfaces genéricas.

    Por ejemplo, si está diseñando una clase que se utilizará para crear elementos en una colección basada en genéricos, puede que tenga que implementar una interfaz como IComparable<T>, donde T es el tipo de la clase.

Para obtener un ejemplo de una clase genérica simple, vea Introducción a los genéricos (Guía de programación de C#)

Las reglas para los parámetros de tipo y las restricciones tienen varias implicaciones en el comportamiento de la clase genérica, especialmente en lo relativo a la herencia y accesibilidad de los miembros. Antes de continuar, debería entender algunos términos. Para una clase genérica Node<T>,, el código cliente puede hacer referencia a la clase especificando un argumento de tipo para crear un tipo construido cerrado (Node<int>). Como alternativa, puede dejar el parámetro de tipo sin especificar, por ejemplo, al especificar una clase base genérica, para crear un tipo construido abierto (Node<T>). Las clases genéricas pueden heredar de clases base construidas cerradas o construidas abiertas:

class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type 
class NodeOpen<T> : BaseNodeGeneric<T> { }

Las clases no genéricas, es decir, concretas, pueden heredar de las clases base construidas cerradas, pero no de las clases construidas abiertas ni de los parámetros de tipo 'naked', ya que no hay ninguna forma de que el código cliente pueda proporcionar el tipo de argumento necesario para crear una instancia de la clase base en tiempo de ejecución.

//No error
class Node1 : BaseNodeGeneric<int> { }

//Generates an error
//class Node2 : BaseNodeGeneric<T> {}

//Generates an error
//class Node3 : T {}

Las clases genéricas que heredan de tipos construidos abiertos deben proporcionar argumentos de tipo para cada parámetro de tipo de clase base no compartidos con la clase que hereda, tal como se muestra en el código siguiente:

class BaseNodeMultiple<T, U> { }

//No error
class Node4<T> : BaseNodeMultiple<T, int> { }

//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }

//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {} 

Las clases genéricas que heredan de tipos construidos abiertos deben especificar restricciones que impliquen o sean un superconjunto de las restricciones sobre el tipo base:

class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }

Los tipos genéricos pueden utilizar varios parámetros y restricciones de tipo, de la forma siguiente:

class SuperKeyType<K, V, U>
    where U : System.IComparable<U>
    where V : new()
{ }

Los tipos construidos abiertos y construidos cerrados se pueden utilizar como parámetros de método:

void Swap<T>(List<T> list1, List<T> list2)
{
    //code to swap items
}

void Swap(List<int> list1, List<int> list2)
{
    //code to swap items
}

Si una clase genérica implementa una interfaz, todas las instancias de esa clase se pueden convertir explícitamente a esa interfaz.

Las clases genéricas son invariables. En otras palabras, si un parámetro de entrada especifica una List<BaseClass>, obtendrá un error de compilación si intenta proporcionar una List<DerivedClass>.

Vea también

Conceptos

Guía de programación de C#

Referencia

Genéricos (Guía de programación de C#)

System.Collections.Generic

Otros recursos

Saving the State of Enumerators

An Inheritance Puzzle, Part One