Elementos genéricos en .NET

Los genéricos permiten personalizar un método, clase, estructura o interfaz con respecto a los datos precisos sobre los que se actúa. Por ejemplo, en lugar de utilizar la clase Hashtable, que permite cualquier tipo de clave y valor, puede utilizar la clase genérica Dictionary<TKey,TValue> y especificar los tipos permitidos para la clave y el valor. Entre las ventajas de los genéricos están una mayor reutilización del código y la seguridad de tipos.

Definición y uso de genéricos

Los genéricos son clases, estructuras, interfaces y métodos que tienen marcadores de posición (parámetros de tipo) para uno o varios de los tipos que almacenan o utilizan. Una clase de colección genérica puede usar un parámetro de tipo como marcador de posición para el tipo de objetos que almacena; los parámetros de tipo aparecen como los tipos de sus campos y los tipos de parámetros de sus métodos. Un método genérico puede usar su parámetro de tipo como el tipo de su valor devuelto o como el tipo de uno de sus parámetros formales. El código siguiente ilustra una definición de clase genérica simple.

generic<typename T>
public ref class Generics
{
public:
    T Field;
};
public class Generic<T>
{
    public T Field;
}
Public Class Generic(Of T)
    Public Field As T

End Class

Cuando cree una instancia de una clase genérica, especifique los tipos reales que hay que sustituir para los parámetros de tipo. Esto establece una nueva clase genérica, a la que se hace referencia como clase genérica construida, con los tipos que elija sustituidos en todas partes en la que aparecen los parámetros de tipo. El resultado es una clase con seguridad de tipos que se adapta a su elección de tipos, como se muestra en el código siguiente.

static void Main()
{
    Generics<String^>^ g = gcnew Generics<String^>();
    g->Field = "A string";
    //...
    Console::WriteLine("Generics.Field           = \"{0}\"", g->Field);
    Console::WriteLine("Generics.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
public static void Main()
{
    Generic<string> g = new Generic<string>();
    g.Field = "A string";
    //...
    Console.WriteLine("Generic.Field           = \"{0}\"", g.Field);
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}
Public Shared Sub Main()
    Dim g As New Generic(Of String)
    g.Field = "A string"
    '...
    Console.WriteLine("Generic.Field           = ""{0}""", g.Field)
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub

Terminología de los genéricos

Los siguientes términos se utilizan para explicar los genéricos en .NET:

  • Una definición de tipo genérico es una clase, estructura o declaración de interfaz que funciona como una plantilla, con marcadores de posición para los tipos que puede contener o utilizar. Por ejemplo, la clase System.Collections.Generic.Dictionary<TKey,TValue> puede contener dos tipos: claves y valores. Dado que una definición de tipo genérico es solo una plantilla, no se pueden crear instancias de una clase, estructura o interfaz que sea una definición de tipo genérico.

  • Losparámetros de tipo genérico, o parámetros de tipo, son los marcadores de posición en una definición de método o tipo genérico. El tipo genérico System.Collections.Generic.Dictionary<TKey,TValue> tiene dos parámetros de tipo, TKey y TValue, que representan los tipos de sus claves y valores.

  • Un tipo genérico construido, o tipo construido, es el resultado de especificar los tipos para los parámetros de tipo genérico de una definición de tipo genérico.

  • Un argumento de tipo genérico es cualquier tipo que se sustituye por un parámetro de tipo genérico.

  • El término general tipo genérico incluye tanto tipos construidos como definiciones de tipo genérico.

  • La covarianza y contravarianza de parámetros de tipo genérico permite usar tipos genéricos construidos, cuyos argumentos de tipo están más derivados (covarianza) o menos derivados (contravarianza) que un tipo construido de destino. La covarianza y la contravarianza se denominan colectivamente varianza. Para obtener más información, vea Covarianza y contravarianza.

  • Lasrestricciones son límites colocados en parámetros de tipo genérico. Por ejemplo, puede limitar un parámetro de tipo a tipos que implementan la interfaz genérica System.Collections.Generic.IComparer<T> para asegurarse de que se pueden ordenar las instancias del tipo. También puede restringir los parámetros de tipo a tipos que tienen una clase base concreta, un constructor sin parámetros o que son tipos de referencia o tipos de valor. Los usuarios del tipo genérico no pueden sustituir los argumentos de tipo que no cumplen con las restricciones.

  • Una definición de método genérico es un método con dos listas de parámetros: una lista de parámetros de tipo genérico y una lista de parámetros formales. Los parámetros de tipo pueden aparecer como el tipo de valor devuelto o como los tipos de los parámetros formales, como se muestra en el siguiente código.

generic<typename T>
T Generic(T arg)
{
    T temp = arg;
    //...
    return temp;
}
T Generic<T>(T arg)
{
    T temp = arg;
    //...
    return temp;
}
Function Generic(Of T)(ByVal arg As T) As T
    Dim temp As T = arg
    '...
    Return temp
End Function

Los métodos genéricos pueden aparecer en tipos genéricos o no genéricos. Es importante tener en cuenta que un método no es genérico solo porque pertenezca a un tipo genérico, ni siquiera porque tenga parámetros formales cuyos tipos sean los parámetros genéricos del tipo envolvente. Un método solo es genérico si tiene su propia lista de parámetros de tipo. En el siguiente código, solo es genérico el método G .

ref class A
{
    generic<typename T>
    T G(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
generic<typename T>
ref class Generic
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
class A
{
    T G<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
class Generic<T>
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
Class A
    Function G(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class
Class Generic(Of T)
    Function M(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class

Ventajas y desventajas de los genéricos

Usar delegados y colecciones genéricas ofrece muchas ventajas:

  • Seguridad de tipos. Los genéricos trasladan al compilador la carga de la seguridad de tipos. No es necesario escribir código para comprobar el tipo de datos correcto porque se hace en tiempo de compilación. Se reduce la necesidad de conversión de tipos y la posibilidad de errores en tiempo de ejecución.

  • Menos código que, además, se reutiliza más fácilmente. No es necesario heredar de un tipo base y reemplazar a los miembros. Por ejemplo, LinkedList<T> está listo para su uso inmediato. Por ejemplo, puede crear una lista vinculada de cadenas con la siguiente declaración de variable:

    LinkedList<String^>^ llist = gcnew LinkedList<String^>();
    
    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Mejor rendimiento. Los tipos de colección genéricos suelen comportarse mejor en el almacenamiento y manipulación de tipos de valor porque no es necesario realizar una conversión boxing de los tipos de valor.

  • Los delegados genéricos permiten devoluciones de llamada con seguridad de tipos sin necesidad de crear varias clases de delegado. Por ejemplo, el delegado genérico Predicate<T> le permite crear un método que implementa sus propios criterios de búsqueda para un tipo concreto y utilizar su método con métodos del tipo Array , como Find, FindLasty FindAll.

  • Los genéricos optimizan el código generado dinámicamente. Cuando se utilizan genéricos con código generado dinámicamente, no es necesario generar el tipo. Esto aumenta el número de escenarios en los que puede utilizar métodos dinámicos ligeros en lugar de generar ensamblados completos. Para obtener más información, vea Cómo: Definir y ejecutar métodos dinámicos y DynamicMethod.

Las siguientes son algunas limitaciones de los genéricos:

  • Los tipos genéricos pueden derivarse de la mayoría de las clases base, como MarshalByRefObject (y pueden utilizarse restricciones para exigir que los parámetros de tipo genérico se deriven de clases base como MarshalByRefObject). Sin embargo, .NET no es compatible con los tipos genéricos enlazados a un contexto. Un tipo genérico puede derivarse de ContextBoundObject, pero al intentar crear una instancia de dicho tipo, se genera una TypeLoadException.

  • Las enumeraciones no pueden tener parámetros de tipo genérico. Una enumeración solo puede ser genérica a propósito (por ejemplo, porque está anidada en un tipo genérico definido mediante Visual Basic, C# o C++). Para más información, vea "Enumeraciones" en Common Type System.

  • Los métodos dinámicos ligeros no pueden ser genéricos.

  • En Visual Basic, C# y C++, no se pueden crear instancias de un tipo anidado incluido en un tipo genérico, a menos que los tipos se hayan asignado a los parámetros de tipo de todos los tipos envolventes. Dicho de otro modo: en la reflexión, un tipo anidado definido mediante estos lenguajes incluye los parámetros de tipo de todos sus tipos envolventes. Esto permite que los parámetros de tipo de tipos envolventes se usen en las definiciones de miembro de un tipo anidado. Para obtener más información, vea “Tipos anidados” en MakeGenericType.

    Nota

    Un tipo anidado definido mediante la emisión de código en un ensamblado dinámico o mediante el uso de Ilasm.exe (Ensamblado de IL) no necesita incluir los parámetros de tipo de sus tipos envolventes. Sin embargo, si no los incluye, los parámetros de tipo no estarán en el ámbito de la clase anidada.

    Para obtener más información, vea “Tipos anidados” en MakeGenericType.

Biblioteca de clases y lenguajes compatibles

.NET ofrece una serie de clases de colección genérica en los espacios de nombres siguientes:

Las interfaces genéricas para implementar comparaciones de orden e igualdad se proporcionan en el espacio de nombres System , junto con los tipos de delegado genérico para controladores de eventos, conversiones y predicados de búsqueda.

Se ha agregado compatibilidad con genéricos a los siguientes espacios de nombres: a System.Reflection para examinar tipos y métodos genéricos; a System.Reflection.Emit para emitir ensamblados dinámicos que contengan tipos y métodos genéricos; y a System.CodeDom para generar gráficos de origen que incluyan genéricos.

Common language runtime proporciona nuevos opcode y prefijos para admitir tipos genéricos en el lenguaje intermedio de Microsoft (MSIL), incluidos Stelem, Ldelem, Unbox_Any, Constrainedy Readonly.

Visual C++, C# y Visual Basic proporcionan compatibilidad completa para definir y utilizar genéricos. Para más información sobre la compatibilidad de lenguaje, consulte Tipos genéricos en Visual Basic, Introducción a los genéricos e Información general sobre genéricos en Visual C++.

Genéricos y tipos anidados

Un tipo que está anidado en un tipo genérico puede depender de los parámetros de tipo del tipo genérico envolvente. Common language runtime considera que los tipos anidados son genéricos, aunque no tengan sus propios parámetros de tipo genérico. Cuando cree una instancia de un tipo anidado, especifique los argumentos de tipo para todos los tipos genéricos envolventes.

Title Descripción
Colecciones genéricas en .NET Describe las clases de colección genérica y otros tipos genéricos en .NET.
Delegados genéricos para manipular matrices y listas Describe los delegados genéricos para conversiones, predicados de búsqueda y acciones realizadas en los elementos de una matriz o colección.
Interfaces genéricas Describe las interfaces genéricas que proporcionan funcionalidad común entre las familias de tipos genéricos.
Covarianza y contravarianza Describe la covarianza y la contravarianza en los parámetros de tipo genérico.
Tipos de colección utilizados normalmente Proporciona información de resumen sobre las características y escenarios de uso de los tipos de colección en .NET, incluidos los tipos genéricos.
Cuándo utilizar colecciones genéricas Describe las reglas generales para determinar cuándo utilizar los tipos de colección genéricos.
Cómo: Definir un tipo genérico con emisión de reflexión Explica cómo generar ensamblados dinámicos que incluyan métodos y tipos genéricos.
Tipos genéricos en Visual Basic Describe los genéricos a los usuarios de Visual Basic e incluye temas sobre el uso y la definición de tipos genéricos.
Introducción a los genéricos Proporciona información general sobre la definición y uso de tipos genéricos para usuarios de C#.
Información general sobre genéricos en Visual C++ Describe los genéricos a los usuarios de C++ e incluye las diferencias entre genéricos y plantillas.

Referencia

System.Collections.Generic

System.Collections.ObjectModel

System.Reflection.Emit.OpCodes