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
- Definición de Genéricos: Permiten definir estructuras de datos y métodos que pueden operar con cualquier tipo de dato.
- 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éricaCaja
con un parámetro de tipoT
.private T contenido;
: Declara una variable de tipoT
.public void Guardar(T item)
: Método que acepta un parámetro de tipoT
y lo guarda encontenido
.public T Obtener()
: Método que devuelve el contenido de tipoT
.
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éricoIntercambiar
que intercambia los valores dea
yb
.
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 queT
debe implementar la interfazIComparable
.
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í.
Curso de Programación en C#
Módulo 1: Introducción a C#
- Introducción a C#
- Configuración del Entorno de Desarrollo
- Programa Hola Mundo
- Sintaxis y Estructura Básica
- Variables y Tipos de Datos
Módulo 2: Estructuras de Control
Módulo 3: Programación Orientada a Objetos
- Clases y Objetos
- Métodos
- Constructores y Destructores
- Herencia
- Polimorfismo
- Encapsulamiento
- Abstracción
Módulo 4: Conceptos Avanzados de C#
- Interfaces
- Delegados y Eventos
- Genéricos
- Colecciones
- LINQ (Consulta Integrada en el Lenguaje)
- Programación Asíncrona
Módulo 5: Trabajando con Datos
Módulo 6: Temas Avanzados
- Reflexión
- Atributos
- Programación Dinámica
- Gestión de Memoria y Recolección de Basura
- Multihilo y Programación Paralela
Módulo 7: Construcción de Aplicaciones
Módulo 8: Mejores Prácticas y Patrones de Diseño
- Estándares de Codificación y Mejores Prácticas
- Patrones de Diseño
- Pruebas Unitarias
- Revisión y Refactorización de Código