CSharp

Tutorial CSharp: Genéricos

Aprende genéricos en C# para crear clases y métodos reutilizables con seguridad de tipos y flexibilidad en programación avanzada.

Aprende CSharp y certifícate

Clases genéricas

Los genéricos en C# nos permiten crear código que funciona con diferentes tipos de datos sin necesidad de duplicarlo. Una clase genérica es una plantilla que puede adaptarse para trabajar con distintos tipos cuando se instancia.

Imagina que quieres crear una caja que pueda almacenar cualquier tipo de objeto: números, textos o incluso objetos personalizados. En lugar de crear una clase diferente para cada tipo, puedes crear una única clase genérica.

Definición de una clase genérica

Para definir una clase genérica, se utiliza el parámetro de tipo entre corchetes angulares <T>. La letra T es una convención que significa "tipo", pero puedes usar cualquier identificador válido:

public class Caja<T>
{
    private T contenido;

    public void Guardar(T item)
    {
        contenido = item;
    }

    public T Obtener()
    {
        return contenido;
    }
}

En este ejemplo, T es un parámetro de tipo que se reemplazará con un tipo real cuando se cree una instancia de la clase.

Uso de clases genéricas

Para usar una clase genérica, debes especificar el tipo concreto al crear la instancia:

// Caja para enteros
Caja<int> cajaNumeros = new Caja<int>();
cajaNumeros.Guardar(10);
int numero = cajaNumeros.Obtener();  // numero = 10

// Caja para cadenas
Caja<string> cajaTextos = new Caja<string>();
cajaTextos.Guardar("Hola mundo");
string texto = cajaTextos.Obtener();  // texto = "Hola mundo"

Múltiples parámetros de tipo

Una clase genérica puede tener más de un parámetro de tipo:

public class Par<T, U>
{
    public T Primero { get; set; }
    public U Segundo { get; set; }

    public Par(T primero, U segundo)
    {
        Primero = primero;
        Segundo = segundo;
    }
}

Uso:

Par<int, string> persona = new Par<int, string>(1, "Juan");
Console.WriteLine($"ID: {persona.Primero}, Nombre: {persona.Segundo}");

Restricciones de tipo

A veces necesitamos limitar los tipos que pueden usarse con nuestra clase genérica. Esto se hace mediante restricciones de tipo:

// T debe ser una clase
public class Repositorio<T> where T : class
{
    // Implementación
}

// T debe tener un constructor sin parámetros
public class Fabrica<T> where T : new()
{
    public T Crear()
    {
        return new T();
    }
}

Las restricciones más comunes son:

  • where T : class - T debe ser un tipo de referencia
  • where T : struct - T debe ser un tipo de valor
  • where T : new() - T debe tener un constructor sin parámetros
  • where T : <nombre-de-interfaz> - T debe implementar la interfaz especificada
  • where T : <nombre-de-clase> - T debe ser o heredar de la clase especificada

Clases genéricas anidadas

También puedes definir clases genéricas dentro de otras clases:

public class Exterior<T>
{
    public class Interior<U>
    {
        public T DatoExterno { get; set; }
        public U DatoInterno { get; set; }
    }
}

Para usar una clase anidada genérica:

Exterior<string>.Interior<int> objeto = new Exterior<string>.Interior<int>();
objeto.DatoExterno = "Texto";
objeto.DatoInterno = 123;

Ventajas de las clases genéricas

  • Reutilización de código: Escribes el código una vez y funciona con múltiples tipos.
  • Seguridad de tipos: El compilador verifica los tipos en tiempo de compilación, reduciendo errores.
  • Rendimiento: Evita conversiones de tipos (casting) y operaciones de boxing/unboxing.

Las clases genéricas son especialmente útiles para crear colecciones, repositorios o cualquier estructura que deba funcionar con diferentes tipos de datos manteniendo la seguridad de tipos.

Métodos genéricos

Los métodos genéricos son una característica de C# que nos permite escribir funciones que pueden trabajar con diferentes tipos de datos sin necesidad de duplicar código. A diferencia de las clases genéricas, que aplican la genericidad a toda la clase, los métodos genéricos permiten aplicar esta flexibilidad a nivel de método individual.

Un método genérico define uno o más parámetros de tipo que se especifican cuando se llama al método. Esto permite crear código más reutilizable y mantenible.

Definición de un método genérico

Para crear un método genérico, se coloca el parámetro de tipo entre corchetes angulares <T> después del nombre del método:

public static void Mostrar<T>(T elemento)
{
    Console.WriteLine($"El elemento es: {elemento}");
}

Este método puede recibir cualquier tipo de dato y mostrarlo en la consola.

Llamada a métodos genéricos

Para llamar a un método genérico, puedes especificar el tipo explícitamente o dejar que el compilador lo infiera:

// Especificando el tipo explícitamente
Mostrar<int>(10);
Mostrar<string>("Hola");

// Dejando que el compilador infiera el tipo (más común)
Mostrar(10);       // El compilador infiere que T es int
Mostrar("Hola");   // El compilador infiere que T es string

La inferencia de tipos hace que el código sea más limpio y legible, ya que el compilador puede determinar automáticamente el tipo basándose en los argumentos pasados.

Métodos genéricos con múltiples parámetros de tipo

Un método genérico puede tener más de un parámetro de tipo:

public static void Intercambiar<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

Este método intercambia los valores de dos variables del mismo tipo:

int x = 5, y = 10;
Intercambiar(ref x, ref y);  // Ahora x = 10, y = 5

string s1 = "Hola", s2 = "Mundo";
Intercambiar(ref s1, ref s2);  // Ahora s1 = "Mundo", s2 = "Hola"

Métodos genéricos con restricciones

Al igual que con las clases genéricas, podemos aplicar restricciones a los parámetros de tipo en los métodos genéricos:

public static T Mayor<T>(T a, T b) where T : IComparable<T>
{
    return a.CompareTo(b) > 0 ? a : b;
}

Este método compara dos valores y devuelve el mayor, pero solo funciona con tipos que implementen la interfaz IComparable<T>:

int resultado = Mayor(5, 10);  // resultado = 10
string texto = Mayor("abc", "xyz");  // texto = "xyz"

Métodos genéricos en clases no genéricas

Una ventaja importante es que podemos definir métodos genéricos dentro de clases no genéricas:

public class Utilidades
{
    public static List<T> ConvertirArray<T>(T[] array)
    {
        return new List<T>(array);
    }
}

Uso:

int[] numeros = { 1, 2, 3, 4, 5 };
List<int> lista = Utilidades.ConvertirArray(numeros);

Métodos genéricos en clases genéricas

También podemos definir métodos genéricos dentro de clases genéricas, incluso con parámetros de tipo diferentes:

public class Contenedor<T>
{
    private T valor;

    public Contenedor(T valor)
    {
        this.valor = valor;
    }

    public U Convertir<U>(Func<T, U> conversor)
    {
        return conversor(valor);
    }
}

Ejemplo de uso:

Contenedor<int> numero = new Contenedor<int>(42);
string texto = numero.Convertir<string>(n => n.ToString());  // "42"
double doble = numero.Convertir(n => n * 2.5);  // 105.0

Casos de uso comunes

Los métodos genéricos son especialmente útiles para:

  • Operaciones matemáticas genéricas:
public static T Sumar<T>(T a, T b) where T : INumber<T>
{
    return a + b;
}
  • Búsqueda de elementos:
public static bool Contiene<T>(T[] array, T elemento)
{
    foreach (T item in array)
    {
        if (item.Equals(elemento))
            return true;
    }
    return false;
}
  • Transformación de datos:
public static List<R> Transformar<T, R>(List<T> lista, Func<T, R> transformador)
{
    List<R> resultado = new List<R>();
    foreach (T item in lista)
    {
        resultado.Add(transformador(item));
    }
    return resultado;
}

Ventajas de los métodos genéricos

  • Reutilización de código: Evita duplicar código para diferentes tipos de datos.
  • Seguridad de tipos: El compilador verifica los tipos en tiempo de compilación.
  • Flexibilidad: Permite escribir algoritmos que funcionan con cualquier tipo que cumpla ciertas restricciones.
  • Rendimiento: Evita conversiones de tipos y operaciones de boxing/unboxing.

Los métodos genéricos son una herramienta fundamental para escribir código más limpio, flexible y reutilizable en C#, permitiendo implementar algoritmos que funcionan con múltiples tipos de datos sin sacrificar la seguridad de tipos.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

30 % DE DESCUENTO

Plan mensual

19.00 /mes

13.30 € /mes

Precio normal mensual: 19 €
63 % DE DESCUENTO

Plan anual

10.00 /mes

7.00 € /mes

Ahorras 144 € al año
Precio normal anual: 120 €
Aprende CSharp online

Ejercicios de esta lección Genéricos

Evalúa tus conocimientos de esta lección Genéricos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de CSharp

Accede a todas las lecciones de CSharp y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A C#

Introducción Y Entorno

Creación De Proyecto C#

Introducción Y Entorno

Variables Y Constantes

Sintaxis

Tipos De Datos

Sintaxis

Operadores

Sintaxis

Control De Flujo

Sintaxis

Funciones

Sintaxis

Estructuras De Control Iterativo

Sintaxis

Interpolación De Strings

Sintaxis

Estructuras De Control Condicional

Sintaxis

Manejo De Valores Nulos

Sintaxis

Clases Y Encapsulación

Programación Orientada A Objetos

Objetos

Programación Orientada A Objetos

Constructores Y Destructores

Programación Orientada A Objetos

Herencia

Programación Orientada A Objetos

Polimorfismo

Programación Orientada A Objetos

Genéricos

Programación Orientada A Objetos

Métodos Virtuales Y Sobrecarga

Programación Orientada A Objetos

Clases Abstractas

Programación Orientada A Objetos

Interfaces

Programación Orientada A Objetos

Propiedades Y Encapsulación

Programación Orientada A Objetos

Métodos De Extensión

Programación Orientada A Objetos

Clases Y Objetos

Programación Orientada A Objetos

Clases Parciales

Programación Orientada A Objetos

Miembros Estáticos

Programación Orientada A Objetos

Tuplas Y Tipos Anónimos

Programación Orientada A Objetos

Arrays Y Listas

Colecciones Y Linq

Diccionarios

Colecciones Y Linq

Conjuntos, Colas Y Pilas

Colecciones Y Linq

Uso De Consultas Linq

Colecciones Y Linq

Linq Avanzado

Colecciones Y Linq

Colas Y Pilas

Colecciones Y Linq

Conjuntos

Colecciones Y Linq

Linq Básico

Colecciones Y Linq

Delegados Funcionales

Programación Funcional

Records

Programación Funcional

Expresiones Lambda

Programación Funcional

Linq Funcional

Programación Funcional

Fundamentos De La Programación Funcional

Programación Funcional

Pattern Matching

Programación Funcional

Testing Unitario Con Xunit

Testing

Excepciones

Excepciones

Delegados

Programación Asíncrona

Eventos

Programación Asíncrona

Lambdas

Programación Asíncrona

Uso De Async Y Await

Programación Asíncrona

Tareas

Programación Asíncrona

Accede GRATIS a CSharp y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender qué son las clases genéricas y cómo definirlas en C#.
  • Aprender a usar parámetros de tipo en clases y métodos genéricos.
  • Conocer las restricciones de tipo aplicables a genéricos para limitar los tipos permitidos.
  • Entender la definición y uso de métodos genéricos, incluyendo inferencia de tipos y múltiples parámetros.
  • Valorar las ventajas de los genéricos en términos de reutilización, seguridad de tipos y rendimiento.