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ícateClases 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 referenciawhere T : struct
- T debe ser un tipo de valorwhere T : new()
- T debe tener un constructor sin parámetroswhere T : <nombre-de-interfaz>
- T debe implementar la interfaz especificadawhere 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.
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.
CRUD en C# de modelo Customer sobre una lista
Arrays y listas
Objetos
Excepciones
Eventos
Lambdas
Diccionarios en C#
Variables y constantes
Tipos de datos
Herencia
Operadores
Uso de consultas LINQ
Clases y encapsulación
Uso de consultas LINQ
Excepciones
Control de flujo
Eventos
Diccionarios
Tipos de datos
Conjuntos, colas y pilas
Lambdas
Conjuntos, colas y pilas
Uso de async y await
Tareas
Constructores y destructores
Operadores
Arrays y listas
Polimorfismo
Polimorfismo
Variables y constantes
Proyecto colecciones y LINQ en C#
Clases y encapsulación
Creación de proyecto C#
Uso de async y await
Funciones
Delegados
Delegados
Constructores y destructores
Objetos
Control de flujo
Funciones
Tareas
Proyecto sintaxis en C#
Herencia C Sharp
OOP en C Sharp
Diccionarios
Introducción a C#
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
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.