Menu
Coddy logo textTech

Attributi e reflection

Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 44 di 70.

Gli attributi sono tag di metadati che associ a elementi del codice come classi, metodi o proprietà. Non modificano direttamente il modo in cui viene eseguito il codice, ma forniscono informazioni che possono essere lette in fase di esecuzione. La reflection è il meccanismo che legge questi metadati.

Applichi un attributo inserendolo tra parentesi quadre sopra l'elemento di destinazione:

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

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

Puoi creare attributi personalizzati ereditando da Attribute:

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

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

La riflessione ti permette di ispezionare i tipi e leggere gli attributi in fase di esecuzione usando lo spazio dei nomi System.Reflection:

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

Puoi anche individuare metodi e proprietà e invocarli dinamicamente:

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

Gli attributi e la reflection sono alla base di molti framework: le librerie di serializzazione li usano per controllare l'output JSON, i framework di testing li usano per individuare i metodi di test e i container per l'iniezione delle dipendenze li usano per collegare automaticamente i servizi.

challenge icon

Sfida

Facile

Creiamo un sistema di rilevamento dei plugin che usa attributi personalizzati e la reflection per trovare e descrivere automaticamente le classi contrassegnate come plugin. È simile al modo in cui i framework reali rilevano componenti, metodi di test o endpoint API in fase di esecuzione.

Organizzerai il codice in tre file:

  • PluginAttribute.cs: crea un attributo personalizzato chiamato PluginAttribute nello spazio dei nomi PluginSystem. Il tuo attributo deve:
    • Ereditare da Attribute
    • Avere due proprietà di sola lettura: Name (string) e Version (string)
    • Accettare entrambi i valori tramite il costruttore
  • Plugins.cs: crea diverse classi di plugin nello spazio dei nomi PluginSystem. Alcune devono essere decorate con il tuo PluginAttribute, mentre una non deve esserlo:
    • ImageProcessor - contrassegnata con [Plugin("Image Processor", "1.0")]
    • DataExporter - contrassegnata con [Plugin("Data Exporter", "2.5")]
    • HelperUtility - NON contrassegnata con alcun attributo (è solo una classe normale)
    Ogni classe può essere vuota: ci interessa solo rilevarne i metadati tramite reflection.
  • Program.cs: nel file principale, usa la reflection per individuare tutte le classi che hanno l'attributo PluginAttribute. Per ogni plugin individuato, stampa le sue informazioni. Dovrai:
    • Ottenere tutti i tipi dall'assembly corrente
    • Verificare la presenza di PluginAttribute su ogni tipo
    • Estrarre e mostrare le proprietà Name e Version dell'attributo

Usa System.Reflection per ispezionare i tipi e recuperare gli attributi personalizzati. Il metodo GetCustomAttribute<T>() restituisce null se l'attributo non è presente, quindi puoi usarlo per filtrare le classi che sono effettivamente plugin.

Stampa ogni plugin individuato in questo formato, uno per riga:

Plugin: {Name} (v{Version})

I plugin devono essere stampati in questo ordine: prima ImageProcessor, poi DataExporter. La classe HelperUtility non deve comparire nell'output perché non ha l'attributo.

Output previsto:

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

Questa sfida mostra come i framework usano gli attributi come marcatori e la reflection per individuarli: la stessa tecnica è alla base dei container di dependency injection, delle librerie di serializzazione e dei framework di test!

Provalo tu

using System;
using System.Reflection;
using PluginSystem;

class Program
{
    public static void Main(string[] args)
    {
        // TODO: Ottieni tutti i tipi dall'assembly corrente
        // Suggerimento: usa Assembly.GetExecutingAssembly().GetTypes()
        
        // TODO: Scorri ogni tipo e verifica se ha il PluginAttribute
        // Suggerimento: usa type.GetCustomAttribute<PluginAttribute>()
        
        // TODO: Se l'attributo esiste, stampa le informazioni del plugin nel formato:
        // Plugin: {Name} (v{Version})
        
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C# online