Menu
Coddy logo textTech

Atributos e Reflection

Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 44 de 70.

Atributos são tags de metadados que você anexa a elementos de código, como classes, métodos ou propriedades. Eles não alteram como o código é executado diretamente, mas fornecem informações que podem ser lidas em tempo de execução. Reflexão é o mecanismo que lê esses metadados.

Você aplica um atributo colocando-o entre colchetes acima do elemento de destino:

[Obsolete("Use NewMethod instead")]
public void OldMethod() { }

[Serializable]
public class Person
{
    public string Name { get; set; }
}

Você pode criar atributos personalizados herdando de Attribute:

public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public AuthorAttribute(string name) => Name = name;
}

[Author("John")]
public class Calculator { }

A Reflection permite inspecionar tipos e ler atributos em tempo de execução usando o namespace System.Reflection:

Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name);  // John

Você também pode descobrir métodos, propriedades e invocá-los dinamicamente:

MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
    Console.WriteLine(method.Name);

Atributos e reflexão impulsionam muitos frameworks - bibliotecas de serialização os utilizam para controlar a saída JSON, frameworks de teste os utilizam para descobrir métodos de teste e contêineres de injeção de dependência os utilizam para conectar serviços automaticamente.

challenge icon

Desafio

Fácil

Vamos construir um sistema de descoberta de plugins que utiliza atributos personalizados e reflexão para encontrar e descrever automaticamente classes marcadas como plugins. Isso é semelhante a como frameworks reais descobrem componentes, métodos de teste ou endpoints de API em tempo de execução.

Você organizará seu código em três arquivos:

  • PluginAttribute.cs: Crie um atributo personalizado chamado PluginAttribute no namespace PluginSystem. Seu atributo deve:
    • Herdar de Attribute
    • Ter duas propriedades somente leitura: Name (string) e Version (string)
    • Aceitar ambos os valores através de seu construtor
  • Plugins.cs: Crie várias classes de plugin no namespace PluginSystem. Algumas devem ser decoradas com seu PluginAttribute, e uma não deve:
    • ImageProcessor - marcada com [Plugin("Image Processor", "1.0")]
    • DataExporter - marcada com [Plugin("Data Exporter", "2.5")]
    • HelperUtility - NÃO marcada com nenhum atributo (esta é apenas uma classe comum)
    Cada classe pode estar vazia - estamos interessados apenas em descobrir seus metadados através de reflexão.
  • Program.cs: Em seu arquivo principal, use reflexão para descobrir todas as classes que possuem o PluginAttribute. Para cada plugin descoberto, imprima suas informações. Você precisará:
    • Obter todos os tipos do assembly atual
    • Verificar cada tipo para o PluginAttribute
    • Extrair e exibir as propriedades Name e Version do atributo

Use System.Reflection para inspecionar tipos e recuperar atributos personalizados. O método GetCustomAttribute<T>() retorna null se o atributo não estiver presente, então você pode usar isso para filtrar quais classes são plugins reais.

Imprima cada plugin descoberto neste formato, um por linha:

Plugin: {Name} (v{Version})

Os plugins devem ser impressos na ordem: ImageProcessor primeiro, depois DataExporter. A classe HelperUtility não deve aparecer na saída, pois não possui o atributo.

Saída esperada:

Plugin: Image Processor (v1.0)
Plugin: Data Exporter (v2.5)

Este desafio demonstra como os frameworks usam atributos como marcadores e reflexão para descobri-los - a mesma técnica alimenta contêineres de injeção de dependência, bibliotecas de serialização e frameworks de teste!

Folha de consulta

Atributos são tags de metadados colocadas entre colchetes acima dos elementos de código. Eles fornecem informações que podem ser lidas em tempo de execução:

[Obsolete("Use NewMethod instead")]
public void OldMethod() { }

[Serializable]
public class Person
{
    public string Name { get; set; }
}

Crie atributos personalizados herdando de Attribute:

public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public AuthorAttribute(string name) => Name = name;
}

[Author("John")]
public class Calculator { }

Reflection permite inspecionar tipos e ler atributos em tempo de execução usando o namespace System.Reflection:

Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name);  // John

Descubra métodos e propriedades dinamicamente:

MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
    Console.WriteLine(method.Name);

O método GetCustomAttribute<T>() retorna null se o atributo não estiver presente, permitindo filtrar tipos com base na presença do atributo.

Experimente você mesmo

using System;
using System.Reflection;
using PluginSystem;

class Program
{
    public static void Main(string[] args)
    {
        // TODO: Obter todos os tipos do assembly atual
        // Dica: Use Assembly.GetExecutingAssembly().GetTypes()
        
        // TODO: Percorrer cada tipo e verificar se ele possui o PluginAttribute
        // Dica: Use type.GetCustomAttribute<PluginAttribute>()
        
        // TODO: Se o atributo existir, imprima as informações do plugin no formato:
        // Plugin: {Name} (v{Version})
        
    }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos