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); // JohnVocê 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.
Desafio
FácilVamos 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 chamadoPluginAttributeno namespacePluginSystem. Seu atributo deve:- Herdar de
Attribute - Ter duas propriedades somente leitura:
Name(string) eVersion(string) - Aceitar ambos os valores através de seu construtor
- Herdar de
Plugins.cs: Crie várias classes de plugin no namespacePluginSystem. Algumas devem ser decoradas com seuPluginAttribute, 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)
Program.cs: Em seu arquivo principal, use reflexão para descobrir todas as classes que possuem oPluginAttribute. 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
NameeVersiondo 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); // JohnDescubra 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})
}
}
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
1Fundamentos de POO
Arquivos ExternosNamespaces e DiretivasIntrodução a Classes e ObjetosA Palavra-chave 'this'Métodos e ParâmetrosCampos vs PropriedadesConstrutoresInicializadores de ObjetosRecapitulação - Calculadora Simples4Herança
Sintaxe Básica de Herança (:)A Palavra-chave 'base'Palavras-chave Virtual e OverrideClasses SeladasA Classe Base 'object'Recapitulação - Hierarquia de Funcionários7Recursos Avançados
Sobrecarga de OperadoresIndexadores (this[])Sobrescrita de ToString()Métodos de ExtensãoRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoSingleton Thread-SafePadrão FactoryPadrão Observer (Eventos)Padrão Strategy2Propriedades e Membros Estáticos
Propriedades AutoimplementadasPropriedades de Apenas Leitura/EscritaCampos e Métodos EstáticosClasses EstáticasMembros com Corpo de Expressão5Polimorfismo e Interfaces
Polimorfismo: Compilação vs ExecuçãoInterface vs Classe AbstrataMúltiplas InterfacesInterfaces ExplícitasUpcasting e DowncastingRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição sobre HerançaGenerics (Classes e Métodos)Delegates e EventosAtributos e ReflectionIDisposable e a Instrução usingFundamentos de Injeção de Dependência11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Arquitetura de Classes
Dados de Instância vs EstáticosPalavras-chave 'readonly' e 'const'Campos de SuporteRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Modificadores de AcessoPropriedades para EncapsulamentoImplementação de Ocultação de DadosPadrões de ImutabilidadeRecapitulação - Registros de Estudantes9Argumentos Variáveis
A Palavra-chave 'params'Parâmetros OpcionaisArgumentos NomeadosSobrecarga de Métodos