Compartir a través de


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 usar la Hashtable clase , que permite que las claves y los valores sean de cualquier tipo, puede usar la Dictionary<TKey,TValue> clase genérica y especificar los tipos permitidos para la clave y el valor. Entre las ventajas de los genéricos están el aumento de la reutilización de 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 usan. Una clase de colección genérica podría 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ámetro 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.

En el código siguiente se muestra una definición de clase genérica simple.

public class SimpleGenericClass<T>
{
    public T Field;
}
Public Class SimpleGenericClass(Of T)
    Public Field As T

End Class

Cuando se crea una instancia de una clase genérica, se especifican los tipos reales que se sustituyen por los parámetros de tipo. Esto establece una nueva clase genérica, denominada clase genérica construida, con los tipos elegidos sustituidos por todas partes en las que aparecen los parámetros de tipo. El resultado es una clase segura para tipos que se adapta a su elección de tipos, como se muestra en el código siguiente.

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

Terminología

Los términos siguientes se usan para analizar genéricos en .NET:

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

  • Los parámetros de tipo genérico, o los parámetros de tipo, son los marcadores de posición en una definición de método o tipo genérico. El System.Collections.Generic.Dictionary<TKey,TValue> tipo genérico 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 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 sustituya por un parámetro de tipo genérico.

  • El término general tipo genérico incluye tipos construidos y definiciones de tipos genéricos.

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

  • Las restricciones son límites colocados en parámetros de tipo genérico. Por ejemplo, puede limitar un parámetro de tipo a los tipos que implementan la System.Collections.Generic.IComparer<T> interfaz genérica para asegurarse de que se pueden ordenar instancias del tipo. También puede restringir los parámetros de tipo a tipos que tienen una clase base determinada, que tienen un constructor sin parámetros o que son tipos de referencia o tipos de valor. Los usuarios del tipo genérico no pueden sustituir argumentos de tipo que no cumplan 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 código siguiente.

    T MyGenericMethod<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
    
    Function MyGenericMethod(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 pertenece a un tipo genérico, o incluso porque tiene parámetros formales cuyos tipos son 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 código siguiente, solo el método G es genérico.

    class A
    {
        T G<T>(T arg)
        {
            T temp = arg;
            //...
            return temp;
        }
    }
    class MyGenericClass<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 MyGenericClass(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

Hay muchas ventajas en el uso de colecciones genéricas y delegados:

  • Seguridad de tipos. Los genéricos desplazan la carga de seguridad de tipos desde usted al compilador. No es necesario escribir código para probar el tipo de datos correcto porque se aplica 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 y mayor reutilización del código. No es necesario heredar de un tipo base ni invalidar miembros. Por ejemplo, el 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 = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Mejor rendimiento. Por lo general, los tipos de colección genéricos funcionan mejor para almacenar y manipular tipos de valor, ya que no es necesario realizar el proceso de boxing de los tipos de valor.

  • Los delegados genéricos permiten invocaciones seguras y específicas de tipos sin necesidad de crear varias clases de delegados. Por ejemplo, el Predicate<T> delegado genérico permite crear un método que implemente sus propios criterios de búsqueda para un tipo determinado y usar el método con métodos del Array tipo como Find, FindLasty FindAll.

  • Los genéricos simplifican el código generado dinámicamente. Cuando se usan 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 usar 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.

A continuación se muestran algunas limitaciones de los genéricos:

  • Los tipos genéricos se pueden derivar de la mayoría de las clases base, como MarshalByRefObject (y las restricciones se pueden usar para requerir que los parámetros de tipo genérico se deriven de clases base como MarshalByRefObject). Sin embargo, .NET no admite tipos genéricos enlazados al contexto. Un tipo genérico se puede derivar de ContextBoundObject, pero al intentar crear una instancia de ese tipo se produce una excepción TypeLoadException.

  • Las enumeraciones no pueden tener parámetros de tipo genérico. Una enumeración solo puede ser genérica de forma incidental (por ejemplo, porque está anidada en un tipo genérico definido mediante Visual Basic, C#o C++). Para obtener 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++, un tipo anidado que está encerrado en un tipo genérico no puede ser instanciado a menos que se hayan asignado tipos a los parámetros de tipo de todos los tipos que lo encierran. Otra manera de decir esto es que, mediante la reflexión, un tipo anidado definido por estos lenguajes incluye los parámetros de tipo de todos sus tipos envolventes. Esto permite que los parámetros de tipo de los tipos que los contienen se utilicen en las definiciones de miembros 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 del Ilasm.exe (Ensamblador IL) no está obligado a incluir los parámetros de tipo de los tipos que lo envuelven; 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 soporte de idioma

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

Las interfaces genéricas para implementar comparaciones de ordenación e igualdad se proporcionan en el espacio de nombres System, junto con tipos de delegados genéricos para manejadores de eventos, conversiones y predicados de búsqueda.

El System.Numerics espacio de nombres proporciona interfaces genéricas para la funcionalidad matemática (disponible en .NET 7 y versiones posteriores). Para obtener más información, consulte Matemáticas genéricas.

Se ha agregado compatibilidad con genéricos al System.Reflection espacio de nombres para examinar tipos genéricos y métodos genéricos, a System.Reflection.Emit para emitir ensamblados dinámicos que contienen tipos y métodos genéricos, y para System.CodeDom generar gráficos de origen que incluyen genéricos.

Common Language Runtime proporciona nuevos códigos de operación y prefijos para admitir tipos genéricos en lenguaje intermedio común (CIL), incluidos Stelem, Ldelem, Unbox_Any, y ConstrainedReadonly.

Visual C++, C# y Visual Basic proporcionan compatibilidad completa para definir y usar genéricos. Para obtener más información sobre la compatibilidad con lenguajes, vea Tipos genéricos en Visual Basic, Introducción a genéricos e Información general sobre genéricos en Visual C++.

Tipos anidados y genéricos

Un tipo 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, incluso si no tienen parámetros de tipo genérico propios. Al crear una instancia de un tipo anidado, debe especificar argumentos de tipo para todos los tipos genéricos envolventes.

Título Descripción
Colecciones genéricas en .NET Describe las clases de colección genéricas y otros tipos genéricos de .NET.
Delegados genéricos para manipular matrices y listas Describe delegados genéricos para conversiones, predicados de búsqueda y acciones que se deben realizar en elementos de una matriz o colección.
Matemáticas genéricas Describe cómo puede realizar operaciones matemáticas de forma genérica.
Interfaces genéricas Describe interfaces genéricas que proporcionan funcionalidad común entre familias de tipos genéricos.
Covarianza y contravarianza Describe la covarianza y la contravarianza en parámetros de tipo genérico.
Tipos de colección usados habitualmente Proporciona información de resumen sobre las características y los escenarios de uso de los tipos de colección en .NET, incluidos los tipos genéricos.
Cuándo usar colecciones genéricas Describe reglas generales para determinar cuándo usar 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 incluyen tipos y métodos genéricos.
Tipos genéricos en Visual Basic Describe la característica de genéricos para los usuarios de Visual Basic, incluidos los temas de instrucciones para usar y definir tipos genéricos.
Introducción a los genéricos Proporciona información general sobre cómo definir y usar tipos genéricos para los usuarios de C#.
Introducción a los genéricos en Visual C++ Describe la característica de genéricos para los usuarios de C++, incluidas las diferencias entre genéricos y plantillas.

Referencia