Menu
Coddy logo textTech

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);  // John

Tambié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.

challenge icon

Desafío

Fácil

Vamos 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 llamado PluginAttribute en el espacio de nombres PluginSystem. Tu atributo debe:
    • Heredar de Attribute
    • Tener dos propiedades de solo lectura: Name (string) y Version (string)
    • Aceptar ambos valores a través de su constructor
  • Plugins.cs: Crea varias clases de complementos en el espacio de nombres PluginSystem. Algunas deben estar decoradas con tu PluginAttribute, 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)
    Cada clase puede estar vacía; solo nos interesa descubrir sus metadatos a través de la reflexión.
  • Program.cs: En tu archivo principal, utiliza la reflexión para descubrir todas las clases que tienen el PluginAttribute. 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 Name y Version del 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);  // John

Descubra 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})
        
    }
}
quiz iconPonte a prueba

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