Generics en C#: para qué sirven y cómo usarlos en la práctica
Los Generics (generalizaciones) son una de las capacidades clave del lenguaje C# que permite escribir código flexible, reutilizable y con seguridad de tipos. En este artículo analizaremos para qué sirven las generalizaciones, cómo funcionan internamente y mostraremos ejemplos prácticos de su uso en proyectos reales.
¿Qué son los Generics y para qué sirven?
Imagina que necesitas implementar un método que intercambie dos valores. Sin generalizaciones, tendrías que escribir versiones separadas para cada tipo de dato:
// Sin Generics — duplicación de códigovoid SwapInt(ref int a, ref int b) { int temp = a; a = b; b = temp; }void SwapString(ref string a, ref string b) { string temp = a; a = b; b = temp; }// ... y así para cada tipoCon Generics este problema se resuelve de manera elegante:
// Con Generics — un método para todos los tiposvoid Swap<T>(ref T a, ref T b){ T temp = a; a = b; b = temp;}
// Usoint x = 1, y = 2;Swap(ref x, ref y); // T = int
string s1 = "Hola", s2 = "Mundo";Swap(ref s1, ref s2); // T = stringPrincipales ventajas de los Generics:
- Seguridad de tipos — los errores de tipo se detectan en tiempo de compilación
- Rendimiento — sin boxing/unboxing para tipos de valor
- Reutilización de código — una clase/método funciona con cualquier tipo
- Legibilidad — el código se vuelve más claro y conciso
Clases y estructuras genéricas
El ejemplo más común de uso de Generics son las colecciones. En lugar de escribir tu propia clase para cada tipo, creamos una generalizada:
public class MyList<T>{ private T[] _items; private int _count;
public MyList(int capacity = 4) { _items = new T[capacity]; _count = 0; }
public void Add(T item) { if (_count == _items.Length) { Array.Resize(ref _items, _items.Length * 2); } _items[_count++] = item; }
public T Get(int index) { if (index < 0 || index >= _count) throw new IndexOutOfRangeException(); return _items[index]; }
public int Count => _count;}
// Usovar numbers = new MyList<int>();numbers.Add(10);numbers.Add(20);
var names = new MyList<string>();names.Add("Alicia");Observa: al crear una instancia, especificamos el tipo concreto entre corchetes angulares <T>. El compilador sustituye este tipo en todos los lugares donde se usa T.
Métodos genéricos y restricciones (constraints)
A veces es necesario imponer restricciones al tipo T. Por ejemplo, para garantizar que el tipo implemente una interfaz determinada o tenga un constructor sin parámetros:
// Restricción: T debe implementar IComparable<T>public T GetMax<T>(T a, T b) where T : IComparable<T>{ return a.CompareTo(b) > 0 ? a : b;}
// Restricción: T debe tener un constructor sin parámetrospublic class Factory<T> where T : new(){ public T Create() { return new T(); }}
// Restricción: T debe ser un tipo de valor (struct)public struct Nullable<T> where T : struct{ private T _value; private bool _hasValue;
public Nullable(T value) { _value = value; _hasValue = true; }
public T Value => _hasValue ? _value : throw new InvalidOperationException(); public bool HasValue => _hasValue;}Tipos principales de restricciones:
where T : class— tipo de referenciawhere T : struct— tipo de valorBlogs
Book Recommendations
Leer también
Deployments Canary en C#: lanzamiento seguro sin tiempo de inactividad
Long Polling vs WebSocket en Swift: qué elegir para iOS en 2025
Despliegue de una aplicación Swift en VPS: guía avanzada
Error de TypeScript string | undefined: cómo solucionarlo y no volverse loco
Programación funcional en Rust: técnicas avanzadas y modismos