Genéricos (clases y métodos)
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 42 de 70.
Los genéricos le permiten escribir clases y métodos que funcionan con cualquier tipo de datos manteniendo la seguridad de tipos. En lugar de escribir clases separadas para IntList, StringList y DoubleList, usted escribe una clase genérica que los maneja a todos.
Una clase genérica utiliza un parámetro de tipo, normalmente llamado T, encerrado entre corchetes angulares:
public class Box<T>
{
private T content;
public void Store(T item) => content = item;
public T Retrieve() => content;
}
// Uso con diferentes tipos
Box<int> intBox = new Box<int>();
intBox.Store(42);
Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");Cuando creas Box<int>, el compilador reemplaza cada T con int. Esto proporciona comprobación de tipos en tiempo de compilación: no puedes almacenar accidentalmente una cadena en una caja int.
Los métodos genéricos funcionan de manera similar, permitiendo que un solo método opere con diferentes tipos:
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // x=2, y=1El compilador infiere el tipo a partir de los argumentos, por lo que no es necesario escribir Swap<int> explícitamente. Los genéricos son la base de colecciones como List<T> y Dictionary<TKey, TValue>, lo que permite un código reutilizable y seguro en cuanto a tipos.
Desafío
FácilVamos a construir un sistema genérico Storage<T> que pueda contener y gestionar elementos de cualquier tipo. Crearás una clase contenedora reutilizable que demuestra el poder de los genéricos: ¡escríbela una vez y úsala con enteros, cadenas o cualquier otro tipo!
Organizarás tu código en dos archivos:
Storage.cs: Crea una clase genéricaStorage<T>en el espacio de nombresDataStorage. Tu contenedor de almacenamiento debe:- Tener un array privado de tipo
Tpara contener los elementos y una propiedadCountque rastree cuántos elementos hay almacenados - Aceptar una capacidad en su constructor para inicializar el array interno
- Tener un método
Add(T item)que añada un elemento si hay espacio - Tener un método
Get(int index)que devuelva el elemento en el índice especificado - Tener un método
GetAll()que devuelva todos los elementos almacenados unidos por", "
- Tener un array privado de tipo
Program.cs: En tu archivo principal, demuestra que tu clase genérica funciona con dos tipos diferentes. Crea unStorage<int>y unStorage<string>, añade elementos a cada uno y muestra que la misma clase funciona perfectamente con ambos tipos.
Recibirás cuatro entradas:
- Primer entero a almacenar
- Segundo entero a almacenar
- Primera cadena a almacenar
- Segunda cadena a almacenar
Crea un almacenamiento de enteros con capacidad 5 y un almacenamiento de cadenas con capacidad 5. Añade los dos enteros al almacenamiento de enteros y las dos cadenas al almacenamiento de cadenas.
Imprime la salida en este formato:
Int storage: {GetAll()}
Int count: {Count}
First int: {Get(0)}
String storage: {GetAll()}
String count: {Count}
First string: {Get(0)}Por ejemplo, si las entradas son 42, 100, Hello, y World, la salida debería ser:
Int storage: 42, 100
Int count: 2
First int: 42
String storage: Hello, World
String count: 2
First string: HelloObserva cómo escribes la clase Storage<T> una sola vez, pero funciona perfectamente tanto con int como con string: ¡esa es la magia de los genéricos! El compilador garantiza la seguridad de tipos, por lo que no puedes añadir accidentalmente una cadena a tu almacenamiento de enteros.
Hoja de referencia
Los genéricos le permiten escribir clases y métodos que funcionan con cualquier tipo de datos manteniendo la seguridad de tipos.
Una clase genérica utiliza un parámetro de tipo (normalmente T) encerrado entre paréntesis angulares:
public class Box<T>
{
private T content;
public void Store(T item) => content = item;
public T Retrieve() => content;
}Al crear una instancia, especifique el tipo para reemplazar a T:
Box<int> intBox = new Box<int>();
intBox.Store(42);
Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");Los métodos genéricos permiten que un solo método opere con diferentes tipos:
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // Compiler infers type from argumentsLos genéricos proporcionan comprobación de tipos en tiempo de compilación y son la base de colecciones como List<T> y Dictionary<TKey, TValue>.
Pruébalo tú mismo
using System;
using DataStorage;
class Program
{
public static void Main(string[] args)
{
// Leer entradas
int num1 = Convert.ToInt32(Console.ReadLine());
int num2 = Convert.ToInt32(Console.ReadLine());
string str1 = Console.ReadLine();
string str2 = Console.ReadLine();
// TODO: Crear un Storage<int> con capacidad 5
// TODO: Agregar los dos enteros al almacenamiento de enteros
// TODO: Crear un Storage<string> con capacidad 5
// TODO: Agregar las dos cadenas al almacenamiento de cadenas
// TODO: Imprimir la salida en el formato requerido
// Almacenamiento de enteros: {GetAll()}
// Cantidad de enteros: {Count}
// Primer entero: {Get(0)}
// Almacenamiento de cadenas: {GetAll()}
// Cantidad de cadenas: {Count}
// Primera cadena: {Get(0)}
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes