Los genéricos en C# son una característica poderosa que permite definir clases, interfaces y métodos con un tipo de dato genérico. Esto proporciona una mayor flexibilidad y reutilización del código, ya que permite trabajar con cualquier tipo de dato sin necesidad de duplicar el código para cada tipo específico.

Conceptos Clave

  1. Definición de Genéricos: Permiten definir estructuras de datos y métodos que pueden operar con cualquier tipo de dato.
  2. Ventajas de los Genéricos:
    • Reutilización del Código: Evita la duplicación de código.
    • Seguridad de Tipo: Detecta errores de tipo en tiempo de compilación.
    • Rendimiento: Elimina la necesidad de conversiones de tipo (boxing/unboxing).

Definición de Clases Genéricas

Una clase genérica se define utilizando un parámetro de tipo. Aquí hay un ejemplo de una clase genérica simple:

public class Caja<T>
{
    private T contenido;

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

    public T Obtener()
    {
        return contenido;
    }
}

Explicación del Código

  • public class Caja<T>: Define una clase genérica Caja con un parámetro de tipo T.
  • private T contenido;: Declara una variable de tipo T.
  • public void Guardar(T item): Método que acepta un parámetro de tipo T y lo guarda en contenido.
  • public T Obtener(): Método que devuelve el contenido de tipo T.

Uso de la Clase Genérica

Caja<int> cajaDeEnteros = new Caja<int>();
cajaDeEnteros.Guardar(123);
int numero = cajaDeEnteros.Obtener();

Caja<string> cajaDeCadenas = new Caja<string>();
cajaDeCadenas.Guardar("Hola Mundo");
string mensaje = cajaDeCadenas.Obtener();

Métodos Genéricos

Los métodos genéricos permiten definir métodos con parámetros de tipo genérico. Aquí hay un ejemplo:

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

Explicación del Código

  • public static void Intercambiar<T>(ref T a, ref T b): Define un método genérico Intercambiar que intercambia los valores de a y b.

Uso del Método Genérico

int x = 1, y = 2;
Utilidades.Intercambiar(ref x, ref y);
// Ahora x es 2 y y es 1

string s1 = "Hola", s2 = "Mundo";
Utilidades.Intercambiar(ref s1, ref s2);
// Ahora s1 es "Mundo" y s2 es "Hola"

Restricciones de Tipo

Las restricciones de tipo permiten especificar los requisitos que debe cumplir un tipo genérico. Aquí hay un ejemplo:

public class Almacen<T> where T : IComparable
{
    private T[] elementos;
    private int indice;

    public Almacen(int tamaño)
    {
        elementos = new T[tamaño];
        indice = 0;
    }

    public void Agregar(T item)
    {
        if (indice < elementos.Length)
        {
            elementos[indice] = item;
            indice++;
        }
    }

    public T Obtener(int i)
    {
        return elementos[i];
    }
}

Explicación del Código

  • where T : IComparable: Restricción que indica que T debe implementar la interfaz IComparable.

Uso de la Clase con Restricciones

Almacen<int> almacenDeEnteros = new Almacen<int>(10);
almacenDeEnteros.Agregar(5);
int valor = almacenDeEnteros.Obtener(0);

Ejercicio Práctico

Ejercicio

Crea una clase genérica Pila<T> que implemente una pila (stack) con las siguientes operaciones:

  • Push(T item): Agrega un elemento a la pila.
  • Pop(): Elimina y devuelve el elemento en la parte superior de la pila.
  • Peek(): Devuelve el elemento en la parte superior de la pila sin eliminarlo.
  • Count: Propiedad que devuelve el número de elementos en la pila.

Solución

public class Pila<T>
{
    private List<T> elementos = new List<T>();

    public void Push(T item)
    {
        elementos.Add(item);
    }

    public T Pop()
    {
        if (elementos.Count == 0)
            throw new InvalidOperationException("La pila está vacía.");
        
        T item = elementos[elementos.Count - 1];
        elementos.RemoveAt(elementos.Count - 1);
        return item;
    }

    public T Peek()
    {
        if (elementos.Count == 0)
            throw new InvalidOperationException("La pila está vacía.");
        
        return elementos[elementos.Count - 1];
    }

    public int Count
    {
        get { return elementos.Count; }
    }
}

Uso de la Clase Pila<T>

Pila<int> pilaDeEnteros = new Pila<int>();
pilaDeEnteros.Push(1);
pilaDeEnteros.Push(2);
pilaDeEnteros.Push(3);

int cima = pilaDeEnteros.Peek(); // Devuelve 3
int elemento = pilaDeEnteros.Pop(); // Devuelve 3 y lo elimina de la pila
int cuenta = pilaDeEnteros.Count; // Devuelve 2

Conclusión

Los genéricos en C# son una herramienta esencial para escribir código flexible, reutilizable y seguro. Permiten trabajar con cualquier tipo de dato sin sacrificar la seguridad de tipo ni el rendimiento. En esta lección, hemos cubierto la definición de clases y métodos genéricos, el uso de restricciones de tipo y hemos implementado una clase genérica Pila<T> como ejercicio práctico.

En el próximo tema, exploraremos las colecciones genéricas en C#, que son una extensión natural de los conceptos que hemos aprendido aquí.

© Copyright 2024. Todos los derechos reservados