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, methods ou propriedades. Eles não alteram diretamente a forma como o código é executado, 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-alvo:

[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 reflexão 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 methods, 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 usam-nos para controlar a saída JSON, frameworks de teste usam-nos para descobrir methods de teste, e contêineres de injeção de dependências usam-nos para conectar serviços automaticamente.

challenge icon

Desafio

Fácil

Vamos criar um sistema de descoberta de plugins que usa atributos personalizados e reflexão para encontrar e descrever automaticamente classes marcadas como plugins. Isso é semelhante à forma 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 por meio 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 por meio da reflexão.
  • Program.cs: no arquivo principal, use reflexão para descobrir todas as classes que têm o PluginAttribute. Para cada plugin descoberto, exiba suas informações. Você precisará:
    • obter todos os tipos do assembly atual
    • verificar cada tipo em busca do 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, portanto você pode usá-lo para filtrar quais classes são plugins de fato.

Exiba cada plugin descoberto neste formato, um por linha:

Plugin: {Name} (v{Version})

Os plugins devem ser exibidos na ordem: primeiro ImageProcessor, 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 é usada em contêineres de injeção de dependência, bibliotecas de serialização e frameworks de testes!

Experimente você mesmo

using System;
using System.Reflection;
using PluginSystem;

class Program
{
    public static void Main(string[] args)
    {
        // TODO: Obtenha todos os tipos do assembly atual
        // Dica: Use Assembly.GetExecutingAssembly().GetTypes()
        
        // TODO: Percorra cada tipo e verifique 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

Pratique por conta própria: Compilador de C# online