Reflection mediante dynamic

Al Supporto Tecnico ci prepariamo in anticipo alle richieste sui prodotti attualmente in beta, i quali arriveranno nei prossimi mesi. Studiando Visual C# 2010 Beta 1 e le nuove feature introdotte nella runtime, ho notato che gli sviluppatori di applicazioni hanno a disposizione un nuova keyword, dynamic, che si basa sulla DLR, Dynamic Language Runtime.

Per la definizione formale e per come il compilatore si comporta durante la fase di compile-time vi rimando alla documentazione on-line che sicuramente esprime questi concetti teorici in modo più chiaro di quanto possa fare io. Open-mouthed

Quel che m’interessa descrivere è l’impatto che questo nuovo costrutto ha sulla Reflection, in particolare su come si sia notevolmente semplificata la scrittura del codice. A tal fine ripercorriamo rapidamente come funzionava la reflection sino alla versione 3.5 della runtime.

Uno sviluppatore doveva interagire con la DLL che definisce il tipo interessato e successivamente invocare le funzioni implementate nella classe tramite InvokeMember. Questo rendeva la stesura del codice sicuramente più articolata di una normale invocazione di funzione, costringendo lo sviluppatore a distinguere tra le istanze realizzate mediante Reflection e le istanze “tradizionali”.

Vediamo di seguito il seguente codice che definisce la libreria ClassLibrary:

 namespace ClassLibrary
{
    public class MyMath
    {
        public  long sum(int x, int y)
        {
            return x + y;
        }
    }
}

Ecco cosa accadeva nelle vecchie edizioni della runtime:

 class Program
{
    static void Main(string[] args)
    {
        Assembly myLibrary = null;
        try
        {
            // Carico la Libreria interessata
            myLibrary = Assembly.LoadFrom("C:\\ClassLibrary.dll");
            // Reperisco la classe con cui interagire
            Type MyMathType = myLibrary.GetType("ClassLibrary.MyMath");
            // Creo un'istanza della classe tramite reflection
            Object MyMathObj = Activator.CreateInstance(MyMathType);
            // Preparo gli argomenti da passare alla funzione
            Object[] methodArgs = new Object[2];
            methodArgs[0] = 2;
            methodArgs[1] = 3;
            // Invoco il  metodo sum indicando l'istanza, i parametri in ingresso ed i flag opportuni
            Console.WriteLine("The sum of 3 + 2 is " + 
                                MyMathType.InvokeMember("sum", BindingFlags.Default | BindingFlags.InvokeMethod,
                                null, MyMathObj, methodArgs).ToString());
        }
        catch(Exception e)
        {
            Console.WriteLine(e.ToString());
        }
        Console.ReadLine();
    }
}

Con i tipi dynamic la stesura di codice mediante la tecnica della reflection è praticamente identica all’invocazione di un metodo per una normale istanza:

 namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //Carico la dll interessata definendo un’istanza    
            dynamic test = Assembly.LoadFrom("C:\\ClassLibrary.dll").CreateInstance("ClassLibrary.MyMath");
            // invoco il metodo tramite reflection, in modo del tutto trasperante
            Console.WriteLine("La somma di 3+2 =" + test.sum(3, 2));
            Console.ReadLine();
        }
    }
}

Non ci siamo notevolmente semplificati la vita? Smile

Carmelo Pulvirenti
Support Engineer
Windows Mobile & Embedded Developer Support
.NET & Visual Studio Technology

Comments

  • Anonymous
    September 19, 2009
    Domanda, con dynamic potremo avere + problemi a runtime? Ciao

  • Anonymous
    September 20, 2009
    Ciao Antonio, Grazie per la domanda. Nella fase di runtime non esistono rischi maggiori di quelli che si corrono con l’implementazione tradizionale della Reflection. In entrambi i casi, infatti, si potrà sollevare, in fase di runtime, un’eccezione dovuta la fatto che il metodo non è presente nelle classe indicata. Quindi vai tranquillo :-) Ciao, Carmelo

  • Anonymous
    September 22, 2009
    Ciao, innanzi tutto complimenti per l' articolo e per il blog in generale! Non mi e' chiara una cosa: nell' esempio di sopra e' chiaro che voglio chiamare la funzione SUM. ma se la funzione da chiamare non e' nota a priori, tipo e' dentro una variabile come posso fare? in altre parole: e' possibile fare qalcosa di simile: string nomeMetodo = "sum"; test[nomeMetodo] (3, 2); Grazie!

  • Anonymous
    September 28, 2009
    Ciao Stefano, L’utilizzo dei dynamic che ho presentato, consente al programmatore di non fare distinzione tra gli oggetti ottenuti mediante Reflection e quelli istanziati tradizionalmente. Per realizzare la funzionalità da te richiesta, dovrai utilizzare il metodo InvokeMember passando tra i parametri il nome del metodo da invocare. Come esempio puoi considerare la prima sezione di codice che ho scritto. Ciao, Carmelo.

  • Anonymous
    December 28, 2009
    Ciao Carmelo, un'altra domanda sull'uso di questa splendida feature: a livello di risorse (complessità computazionale e memoria allocata) si ha un risparmio, un peggioramento o rimane tutto sostanzialmente uguale? penso ai casi in cui la reflection venga utilizzata in modo pesante ..

  • Anonymous
    February 03, 2010
    Ciao Alberto, In generale invocare un metodo mediante la Reflection implica una riduzione delle perfomance di circa 7-8 volte rispetto un’invocazione “tradizionale”.Questo ovviamente è un risultato atteso per ovvie ragioni di compilazione. I dynamic non alterano le prestazioni del nostro software, ma ci facilitano la stesura delle nostre applicazioni. Saluti.