top of page

Introduzione alla Reflection in C#


La Reflection è una delle funzionalità del .NET Framework che può avere un notevole impatto nello sviluppo di applicazioni avanzate. Si tratta sostanzialmente di un potente modo di estrapolare e manipolare a runtime le informazioni presenti negli assembly ed nei metadati di un’ applicazione (i metadati contengono tutte le informazioni sui tipi degli oggetti usati da un’applicazione e la possibilità di ottenere tali informazioni può rivelarsi quindi molto vantaggiosa per i programmatori).

Con la Reflection è possibile creare dinamicamente un’istanza di un tipo, ottenere il tipo di un oggetto esistente, invocare i suoi metodi o accedere ai suoi campi e proprietà. Inoltre se nell’applicazione vengono utilizzati gli Attributi, tramite la Reflection è possibile accedervi.

La classe del Microsoft .NET Framework che ci permette di gestire gli oggetti degli assembly a run-time è la System.Reflection, che con facilità ci consente anche di recuperare le risorse contenute negli oggetti e nei moduli caricati (ovvero tutti gli altri file che durante la fase di compilazione vengono inseriti nell’eseguibile principale o nella libreria).

La classe Type è la base di tutte le possibili operazioni. Essa è una classe astratta che funge da tramite per accedere ai metadati attraverso le classi della Reflection. Utilizzando un oggetto Type è possibile ottenere molte informazioni relative a costruttori, metodi, campi, proprietà, eventi e dettagli implementativi in genere di una classe.

Vediamo ora i differenti modi in cui può essere utilizzata la Reflection:

Classe Assembly: può essere utilizzata per ottenere informazioni e manipolare gli assembly. E’ possibile tramite essa effettuare diverse operazioni. GetType e GetTypes sono i due metodi più frequentemente utilizzati. public class Test { private int numero; public Test(int f) { numero = f; }

public int MetodoTest(int x) { Console.WriteLine("\nTest.MetodoTest({0}) in esecuzione.", x); return x * numero; }

public static void Main() { Assembly assem = Assembly.GetExecutingAssembly(); Console.WriteLine("Nome dell’assembly:"); Console.WriteLine(assem.FullName);

AssemblyName assemName = assem.GetName(); Console.WriteLine("\nNome: {0}", assemName.Name); Console.WriteLine("Versione: {0}.{1}", assemName.Version.Major, assemName.Version.Minor);

Console.WriteLine("\nAssembly CodeBase:"); Console.WriteLine(assem.CodeBase);

// Crea un oggetto tramite l’assembly Object o = assem.CreateInstance("Test", false, BindingFlags.ExactBinding, null, new Object[] { 2 }, null, null); //Permette di ottenere informazioni sulla chiamata di un metodo MethodInfo m = assem.GetType("Test").GetMethod("MetodoTest"); Object ret = m.Invoke(o, new Object[] { 42 }); Console.WriteLine("MetodoTest ha restituito {0}.", ret);

Console.WriteLine("\nAssembly entry point:"); Console.WriteLine(assem.EntryPoint); } }

Nell’esempio precedente sarebbe possibile ottenere diverse altre informazioni. Ad esempio potremmo utilizzare il metodo Type.GetMembers per ottenere un array di oggetti MemberInfo che descrivono ciascuno dei membri del tipo corrente

Classe Module: è utilizzata per la reflection dei moduli. E’ possibile tramite essa ottenere informazioni sugli assembly padre, sulle classi presenti nei moduli e su tutti i metodi globali disponibili nelle classi. Test c1 = new Test (); // Mostra il modulo corrente Module m = c1.GetType().Module; Console.WriteLine("Il modulo corrente è {0}.", m.Name);

// Elenca tutti i moduli presenti nell’assembly Assembly curAssembly = Assembly.GetExecutingAssembly(); Console.WriteLine("L’assembly correntemente in esecuzione è {0}.", curAssembly);

Module[] mods = curAssembly.GetModules(); foreach (Module md in mods) { Console.WriteLine("Questo assembly contiene il modulo {0} ", md.Name); }

Classe MethodInfo: è utilizzata per ottenere informazioni quali nome, tipo di dati restituito, parametri, modificatori di accesso (i vari private, public, protected) e dettagli implementativi di ciascun metodo all’interno di una classe, modulo o assembly. I metodi da utilizzare sono GetMethod e GetMethods.class Test { static void Main(string[] args) { Type type = typeof(Test); MethodInfo [] methodInfos = type.GetMethods();

foreach (MethodInfo methodInfo in methodInfos) { // Informazioni sul metodo Console.WriteLine("Nome: {0} + "\n" + , Pubblico: {1} + "\n" +, Statico: {2} + "\n" +, Tipo Ritornato: {3}", methodInfo.Name, methodInfo.IsPublic, methodInfo.IsStatic, methodInfo.ReturnType.FullName);

// Informazioni sui parametri del metodo ParameterInfo [] paramInfos = methodInfo.GetParameters(); foreach (ParameterInfo paramInfo in paramInfos) { Console.WriteLine("Nome Parametro: {0}, Tipo Parametro: {1}", paramInfo.Name, paramInfo.ParameterType.FullName); } } Console.ReadLine(); }

public void Metodo1() { Console.WriteLine("Ciao”); }

public int Addizione(int x, int y) { return x + y; } } }

Classe FieldInfo: come suggerisce il nome questa classe viene utilizzata per ottenere informazioni circa il nome, i modificatori di accesso ed i dettagli implementativi di un campo. Spesso utilizzata per settare i valori dei campi di una classe.FieldInfo[] myFieldInfo; Type myType = typeof(Test);

// Ottiene i campi delle classe Test myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); Console.WriteLine("\nI campi della classe " + "Test sono \n"); for(int i = 0; i < myFieldInfo.Length; i++) { Console.WriteLine("\nName: {0}", myFieldInfo[i].Name); Console.WriteLine("Declaring Type: {0}", myFieldInfo[i].DeclaringType); Console.WriteLine("Public: {0}", myFieldInfo[i].IsPublic); Console.WriteLine("Member Type: {0}", myFieldInfo[i].MemberType); Console.WriteLine("Field Type: {0}", myFieldInfo[i].FieldType); Console.WriteLine("Family: {0}", myFieldInfo[i].IsFamily); }

Classe EventInfo: è utilizzata per ottenere informazioni quali nome, tipo di dati, attributi personalizzati (custom) e altri dettagli di un evento. Viene anche utilizzata per aggiungere o rimuovere i gestori degli eventi (event handlers). Type testTypeBindingFlags = typeof(System.Windows.Forms.Button);

EventInfo testEventBindingFlags = testTypeBindingFlags.GetEvent("Click", BindingFlags.Public);

if(testEventBindingFlags != null) { Console.WriteLine("Evento Click della classe Button."); Console.WriteLine(testEventBindingFlags.ToString()); }

Classe ParameterInfo: è utilizzata per ottenere informazioni sui parametri. Oltre al nome ed al tipo dati fornisce informazioni sul tipo di parametro (input o output) e sulla sua posizione nella lista di parametri di un metodo. Utilizzando il metodo GetParameters possiamo ottenere un array di oggetti ParameterInfo che rappresentano, in ordine, i parametri di un metodo.//Supponiamo di avere ottenuto un array di oggetti //MemberInfo (membersInfo) di un assembly foreach (MemberInfo mi in membersInfo) { Console.WriteLine ("Member: {0}", mi.Name);

//Se l’oggetto è un metodo vengono visualizzate //le informazioni sui suoi parametri if (mi.MemberType==MemberTypes.Method) { foreach ( ParameterInfo pi in ((MethodInfo) mi).GetParameters() ) { Console.WriteLine ("Parametro: Tipo={0}, Nome={1}", pi.ParameterType, pi.Name); } }

Un altro importante namespace è il System.Reflection.Emit che può essere utilizzato per creare un tipo a runtime e richiamarlo come e quando si desidera.

Quelli riportati sono solo semplici esempi di Reflection ma capirete come essa sia un potente strumento in mano ai programmatori per sviluppare applicazioni sempre più efficienti e performanti.

bottom of page