Atributos y Reflexión
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 44 de 70.
Los Atributos son etiquetas de metadatos que se adjuntan a elementos de código como clases, métodos o propiedades. No cambian la forma en que el código se ejecuta directamente, pero proporcionan información que puede ser leída en tiempo de ejecución. La Reflexión es el mecanismo que lee estos metadatos.
Se aplica un atributo colocándolo entre corchetes encima del elemento de destino:
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Serializable]
public class Person
{
public string Name { get; set; }
}Puedes crear atributos personalizados heredando de Attribute:
public class AuthorAttribute : Attribute
{
public string Name { get; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John")]
public class Calculator { }La reflexión permite inspeccionar tipos y leer atributos en tiempo de ejecución utilizando el espacio de nombres System.Reflection:
Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name); // JohnTambién puedes descubrir métodos, propiedades e invocarlos dinámicamente:
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
Console.WriteLine(method.Name);Los atributos y la reflexión potencian muchos frameworks: las bibliotecas de serialización los utilizan para controlar la salida JSON, los frameworks de pruebas los usan para descubrir métodos de prueba y los contenedores de inyección de dependencias los emplean para conectar servicios automáticamente.
Desafío
FácilVamos a construir un sistema de descubrimiento de complementos (plugins) que utiliza atributos personalizados y reflexión para encontrar y describir automáticamente las clases marcadas como complementos. Esto es similar a cómo los frameworks reales descubren componentes, métodos de prueba o puntos finales de API en tiempo de ejecución.
Organizarás tu código en tres archivos:
PluginAttribute.cs: Crea un atributo personalizado llamadoPluginAttributeen el espacio de nombresPluginSystem. Tu atributo debe:- Heredar de
Attribute - Tener dos propiedades de solo lectura:
Name(string) yVersion(string) - Aceptar ambos valores a través de su constructor
- Heredar de
Plugins.cs: Crea varias clases de complementos en el espacio de nombresPluginSystem. Algunas deben estar decoradas con tuPluginAttribute, y una no:ImageProcessor- marcada con[Plugin("Image Processor", "1.0")]DataExporter- marcada con[Plugin("Data Exporter", "2.5")]HelperUtility- NO marcada con ningún atributo (esta es solo una clase regular)
Program.cs: En tu archivo principal, utiliza la reflexión para descubrir todas las clases que tienen elPluginAttribute. Para cada complemento descubierto, imprime su información. Necesitarás:- Obtener todos los tipos del ensamblado actual
- Verificar cada tipo para buscar el
PluginAttribute - Extraer y mostrar las propiedades
NameyVersiondel atributo
Usa System.Reflection para inspeccionar tipos y recuperar atributos personalizados. El método GetCustomAttribute<T>() devuelve null si el atributo no está presente, por lo que puedes usar esto para filtrar qué clases son complementos reales.
Imprime cada complemento descubierto en este formato, uno por línea:
Plugin: {Name} (v{Version})Los complementos deben imprimirse en el orden: ImageProcessor primero, luego DataExporter. La clase HelperUtility no debe aparecer en la salida ya que carece del atributo.
Salida esperada:
Plugin: Image Processor (v1.0)
Plugin: Data Exporter (v2.5)Este desafío demuestra cómo los frameworks utilizan atributos como marcadores y la reflexión para descubrirlos; ¡la misma técnica impulsa los contenedores de inyección de dependencias, las bibliotecas de serialización y los frameworks de pruebas!
Hoja de referencia
Los atributos son etiquetas de metadatos colocadas entre corchetes encima de los elementos de código. Proporcionan información que se puede leer en tiempo de ejecución:
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Serializable]
public class Person
{
public string Name { get; set; }
}Cree atributos personalizados heredando de Attribute:
public class AuthorAttribute : Attribute
{
public string Name { get; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John")]
public class Calculator { }La reflexión le permite inspeccionar tipos y leer atributos en tiempo de ejecución utilizando el espacio de nombres System.Reflection:
Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name); // JohnDescubra métodos y propiedades de forma dinámica:
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
Console.WriteLine(method.Name);El método GetCustomAttribute<T>() devuelve null si el atributo no está presente, lo que le permite filtrar tipos según la presencia del atributo.
Pruébalo tú mismo
using System;
using System.Reflection;
using PluginSystem;
class Program
{
public static void Main(string[] args)
{
// TODO: Obtener todos los tipos del ensamblado actual
// Sugerencia: Usar Assembly.GetExecutingAssembly().GetTypes()
// TODO: Recorrer cada tipo y verificar si tiene el PluginAttribute
// Sugerencia: Usar type.GetCustomAttribute<PluginAttribute>()
// TODO: Si el atributo existe, imprimir la información del complemento en el formato:
// Plugin: {Name} (v{Version})
}
}
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