Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Introduzione alla Reflection

Un esempio concreto di utilizzo della Reflection per ispezionare un oggetto a runtime
Un esempio concreto di utilizzo della Reflection per ispezionare un oggetto a runtime
Link copiato negli appunti

La Reflection, o introspezione, è un meccanismo che permette di ispezionare un oggetto a runtime, e scoprire quali siano i tipi in esso contenuti, i relativi campi, le proprietà e i metodi.

L'aspetto più interessante è la possibilità di invocare direttamente i metodi di una classe, scoprire gli argomenti passati ad un metodo, accedere alle proprietà della classe, siano essi pubblici o privati.

La Reflection risulta molto utile per la creazione di applicazioni a componenti, per la creazione di ispezionatori di classi, di tool per la costruzione di interfacce grafiche o di debuggers.

Il concetto di Reflection non appartiene solamente a .NET, infatti è stato implementato anche in JAVA, Eiffel, SmallTalk e C++. Ovviamente l'implementazione della Reflection è stata effettuata con modalità differenti a seconda del linguaggio.

Il .NET Framework, tramite il namespace System.Reflection, fornisce una serie di Api per recuperare le informazioni relative all'oggetto durante l'esecuzione, senza dover disporre del nome della classe o della sua struttura.

Con la Reflection è infatti possibile:

  • Determinare la classe di un oggetto
  • Ottenere informazioni riguardo i campi, metodi e le proprietà
  • Scoprire le dichiarazioni dei metodi
  • Instanziare una classe scoprendone il nome solamente a runtime
  • Leggere ed impostare i valori delle proprietà degli oggetti anche se il nome è sconosciuto al proprio programma fino a quando non si esegue
  • Invocare il metodi di un oggetto pur non conoscendo tale metodo fino a quando non si esegue

In generale la Reflection non andrebbe mai utilizzata con leggerezza. Il suo utilizzo è riservato a specifici campi applicativi pertanto non bisogna scomodare la Reflection per lavori in cui sono sufficienti gli strumenti forniti da .NET, questo anche per agevolare le operazioni di debug e per il mantenimento del codice.

Una classe da ispezionare

Per realizzare l'esempio creiamo una classe che ispezioneremo in seconda battuta. Generiamo quindi una classe "autonoleggio.dll", possiamo farlo attraverso Visual Studio, con le versioni Express di Visual C# VB.NET etc. oppure compilando a riga di comando il codice. La classe Auto è composta di tre proprietà ed un metodo.

Listato 1. Definizione della classe Auto (versione VB.NET)

namespace autonoleggio
{
  public class Auto
  {
    public Auto() { }

    private string _modello;
    private string _marca;
    private decimal _costo;

    // Proprietà
    public string Modello { get { return _modello; } set { _modello = value; } }

    public string Marca { get { return _marca; } set { _marca = value; } }

    public decimal Costo { get { return _costo; } set { _costo = value; } }

    // Metodi
    public decimal CalcolaCostoNoleggio(int numGiorni)
    {
      return Costo * numGiorni;
    }
  }
}

Caricare la classe

Per utilizzare la Reflection la prima cosa da fare è includere il relativo namespace ovvero: System.Reflection. Fatto questo, dobbiamo caricare la classe per poterla ispezionare.

Il metodo che utilizziamo è Assembly.LoadFrom(string assemblyFile), che carica in memoria l'assembly passato come parametro alla funzione e restituisce un'istanza della stessa classe Assembly.

Una volta caricata l'istanza della nostra classe assembly, cerchiamo di ottenere le informazioni ed iniziamo col recuperare i tipi in essa contenuti. Lo facciamo utilizzando i metodi:

  • Assembly.GetTypes() che restituisce un array di tipi
  • Assembly.GetType(String) che restituisce i dettagli del tipo passato come parametro.

Le informazioni che si possono recuperare tramite Assembly.GetType() sono Name, Namespace, IsClass, IsPublic e molte altre.

Nel nostro esempio estrarremo gli elementi della classe Type scrivendo due nuovi metodi:

  • GetProperties() che restituisce una array di oggetti System.Reflection.PropertyInfo;
  • GetMethods() che restituisce un array di oggetti System.Reflection.MethodInfo.

La Web Form per il test

Per provare il funzionamento del codice creiamo una pagina .aspx (default.aspx) che contiene un pulsante (Button1). Al click sul pulsante eseguiamo la procedura Test(), che stamperà a video il nome della classe, le tre proprietà e il metodo.

Listato 2. La procedura Test() nel dettaglio (versione VB.NET)

private void Test()
{
  try {
    string pathAsm = Server.MapPath("bin/autonoleggio.dll");
    Assembly extAssembly = Assembly.LoadFrom(pathAsm);
    
    Type[] types = extAssembly.GetTypes();
    
    foreach(Type t in types)
    {
      Response.Write("<strong>Nome</strong><br />");
      Response.Write(t.FullName + "<br />");
      Response.Write("<strong>Proprietà</strong><br />");
      GetProperties(t);
      Response.Write("<strong>Metodi</strong><br />");
      GetMethods(t);
    }
  } catch(Exception e) {
    Response.Write(e.Message);
  }
}

Nella procedura Test(), dopo aver caricato la nostra classe assembly in memoria ed aver recuperato un'istanza della stessa classe Assembly elenchiamo in un ciclo il nome, le proprietà ed i metodi, utilizzando i metodi GetProperties e GetMethods.

Listato 3. Il metodo GetProperties (versione VB.NET)

public void GetProperties(Type theType)
{
  // Prende le proprietà dell'oggetto
  PropertyInfo[] myProperties = theType.GetProperties((BindingFlags.Public | BindingFlags.Instance));

  // Loop che esamina le proprietà
  foreach(PropertyInfo item in myProperties)
  {
    if(theType.IsPublic)
    {
      Response.Write(item.Name + " è di tipo " + item.PropertyType.ToString() + " e ");
      
      // determina se la proprietà può essere letta
      if(item.CanRead)
        Response.Write(" può essere letta e ");
      else
        Response.Write("non può essere letta e ");

      // determina se la proprietà può essere scritta
      if(item.CanWrite)
        Response.Write(" può essere scritta.");
      else
        Response.Write(" non può essere scritta.");
    }
    Response.Write("<br />");
  }
}

Listato 4. Il metodo GetMethods (versione VB.NET)

public void GetMethods(Type theType)
{
  //Legge i metodi dell'oggetto
  MethodInfo[] myMethods = theType.GetMethods();

  foreach (MethodInfo item in myMethods)
  {
    if (theType.IsPublic)
    {
      Response.Write(item.Name + " restituisce " + item.ReturnType.ToString() + "<br />");

      // determina se il metodo è pubblico o privato
      if(item.IsPrivate)
        Response.Write(" è privato e non può essere eseguito " + "<br />");
        
      if (item.IsConstructor)
        Response.Write(" ed è il costruttore per " + theType.Name + "<br />");        

      Response.Write("<br />");
    }
  }
}

Una volta lanciato il test e cliccato sul pulsante apparirà la nostra pagina web con la descrizione dettagliata della classe.

Figura 1. Le informazioni della classe
Le informazioni della classe

Conclusioni

In questo articolo abbiamo introdotto il concetto di Reflection illustrando attraverso un esempio pratico come sia possibile recuperare le informazioni a runtime di una classe.

Questo contributo vuole essere una base di partenza per conoscere le potenzialità della Reflection. I lettori più attenti avranno anche notato come una persona esperta potrebbe ricostruire il codice sorgente (o frammenti di codice per poterlo ricostruire) a partire dall'eseguibile. La Reflection presenta importanti potenzialità, pertanto ogni sviluppatore dovrebbe conoscerne le caratteristiche e sapere in quali circostanze applicarlo per es. per la creazione di ispezionatori di classi, di debuggers o più in generale per la creazione di applicazioni a componenti.

Ti consigliamo anche