English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Generics de C#

Un tipo genérico es una forma general, no una forma específica. En C#, un tipo genérico significa no específico de un tipo de datos específico.

C# le permite definir clases genéricas, interfaces, clases abstractas, campos, métodos, métodos estáticos, propiedades, eventos, delegados y operadores sin usar tipos de datos específicos. Los parámetros de tipo son marcadores de posición específicos de tipos que se especifican cuando se crea una instancia de un tipo genérico.

Se declara una clase genérica especificando un parámetro de tipo en los corchetes angulares después del nombre del tipo, por ejemplo, TypeName<T>, donde T es el parámetro de tipo.

Clase genérica

Las clases genéricas se definen utilizando parámetros de tipo en los corchetes angulares después del nombre de la clase. A continuación se define una clase genérica.

class DataStore<T>
{
    public T Data { get; set; }
}

En el ejemplo anterior, DataStore es una clase genérica. T se llama parámetro de tipo y se puede usar como campo, propiedad, parámetro de método, tipo de retorno y tipo de delegado en la clase DataStore. Por ejemplo, Data es una propiedad genérica porque utilizamos el parámetro de tipo T como su tipo, en lugar de un tipo de datos específico.

Nota: Por lo general, se puede usar T cuando hay solo un parámetro de tipo. Si hay varios tipos de parámetros, no utilice T como parámetro de tipo; puede asignar cualquier nombre a los parámetros de tipo. Se recomienda usar nombres de parámetros de tipo más legibles según las necesidades, como TSession, TKey, TValue, etc.

También puede definir varios parámetros de tipo y separarlos con comas.

class KeyValuePair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
}

Instanciación de clase genérica

Se puede crear una instancia de la clase genérica especificando el tipo real entre corchetes angulares. A continuación se crea una instancia de la clase genérica DataStore.

DataStore<string> store = new DataStore<string>();

Arriba, especificamos el tipo string entre corchetes angulares al crear la instancia. Por lo tanto, T se reemplazará por cualquier tipo T utilizado en toda la clase en tiempo de compilación. Por lo tanto, el tipo del atributo Data es string.

La siguiente imagen ilustra cómo funciona el genérico.

Se puede asignar un valor de cadena al atributo Data. Intentar asignar un valor diferente a una cadena causará un error en tiempo de compilación.

DataStore<string> store = new DataStore<string>();
store.Data = "Hello World!";//obj.Data = 123; //Error en tiempo de compilación

Se puede especificar diferentes tipos de datos para diferentes objetos, como se muestra a continuación.

DataStore<string> strStore = new DataStore<string>();
strStore.Data = "Hello World!";
//strStore.Data = 123; // Error en tiempo de compilación
DataStore<int> intStore = new DataStore<int>();
intStore.Data = 100;
//intStore.Data = "Hello World!"; // Error en tiempo de compilación
KeyValuePair<int, string> kvp1 = new KeyValuePair<int, string>();
kvp1.Key = 100;
kvp1.Value = "Ciento";
KeyValuePair<string, string> kvp2 = new KeyValuePair<string, string>();
kvp2.Key = "IT";
kvp2.Value = "Information Technology";

Características de la clase genérica

  • Las clases genéricas aumentan la reutilización. Cuantos más tipos, mayor es la reutilización. Sin embargo, una sobregeneralización puede hacer que el código sea difícil de entender y mantener.

  • Una clase genérica puede ser la clase base de otros clases genéricas o no genéricas o clases abstractas.

  • Una clase genérica puede derivarse de otros interfaces genéricos o no genéricos, clases o clases abstractas.

Campo genérico

Una clase genérica puede contener campos genéricos, pero no se puede inicializar.

class DataStore<T>
{
    public T data;
}

A continuación, se declara un arreglo genérico.

class DataStore<T>
{
    public T[] data = new T[10];
}

Métodos genéricos

Un método que declara su tipo de retorno o parámetros utilizando un parámetro de tipo se llama método genérico.

class DataStore<T>
{
    private T[] _data = new T[10];
    
    public void AddOrUpdate(int index, T item)
    {
        if(index >= 0 && index < 10)
            _data[index] = item;
    }
    public T GetData(int index)
    {
        if(index >= 0 && index < 10)
            return _data[index];
        else 
            return default(T);
    }
}

Los métodos AddorUpdate() y GetData() son métodos genéricos. El tipo de datos real del parámetro item se especificará en la instanciación de la clase DataStore<T>, como se muestra a continuación.

DataStore<string> cities = new DataStore<string>();
cities.AddOrUpdate(0, "Mumbai");
cities.AddOrUpdate(1, "Chicago");
cities.AddOrUpdate(2, "London");
DataStore<int> empIds = new DataStore<int>();
empIds.AddOrUpdate(0, 50);
empIds.AddOrUpdate(1, 65);
empIds.AddOrUpdate(2, 89);

Los parámetros de tipo genérico pueden utilizarse junto con varios parámetros con o sin parámetros no genéricos y tipos de retorno. A continuación se muestra una sobrecarga de métodos genéricos válida.

public void AddOrUpdate(int index, T data) { }
public void AddOrUpdate(T data1, T data2) { }
public void AddOrUpdate<U>(T data1, U data2) { }
public void AddOrUpdate(T data) { }

Al especificar el tipo de parámetro utilizando el nombre del método dentro de los corchetes angulares, las clases no genéricas pueden contener métodos genéricos, como se muestra a continuación.

class Printer
{
    public void Print<T>(T data)
    {
        Console.WriteLine(data);
    }
}
Printer printer = new Printer();
printer.Print<int>(100);
printer.Print(200); // Inferencia de valores específicos
printer.Print<string>("Hello");
printer.Print("World!"); // Inferencia de valores específicos

Ventajas de los generics

  1. Los generics mejoran la reutilización del código. No es necesario escribir código para manejar diferentes tipos de datos.

  2. Los generics son seguros en términos de tipo. Si intenta usar un tipo de datos diferente al especificado en la definición, se producirá un error en tiempo de compilación.

  3. Los generics tienen una ventaja de rendimiento porque eliminan la posibilidad de empacado y desempacado.