Metodi dinamici di delegazione
Questi giorni sto giochicchiando con i DynamicMethod di .Net e ho pensato di condividere. Lo scopo di tali classi - conosciute anche come lightweight code generation - è di generare codice IL (il P-code di .Net) dinamicamente. Accoppiandoli con i metodi delegate (puntatori a funzione) si possono fare cose veramente interessanti. Gli esempi che seguono sono alquanto banali (il classico "Hello World"), ma con la LCG si possono fare cose moooolto deliziose: esempio 1, esempio 2.
Altrettanto interessante è il funzionamento della Virtual Machine stack based di .Net: il set di istruzioni è documentato qui ma se si vuole giocare seriamente a questo tipo di giochi pericolosissimi (roba impossibile da debuggare capace di creare codice a fungo atomico in men che non si dica) consiglio _IL_ testo di riferimento che è "Compiling for the .Net Language Runtime" il cui unico neo è quello di essere basato sulla CLR 1.0 (niente template e altre corbellerie "moderne"). Volendo, la via più breve per capire l'IL da generare, è l'uso "a posteriori" di reflector.
Ed ora gli esempi, giusto per stimolare l'appetito:
using System; using System.Reflection; using System.Reflection.Emit; public class TestClass { string message; public delegate void TestMethod(); TestMethod myTestMethod; public TestMethod MyTestMethod { get { return myTestMethod; } } public TestClass(string aMessage) { this.message = aMessage; } public void RealTestMethod() { Console.WriteLine(this.message); } public void CreateMethod() { /* * this is equivalent to: * Console.WriteLine("Hello world!"); * there is no access to object members * so the method can be declared as 'static' * */ // declare the method signature as void HelloWorld() // DynamicMethod dm = new DynamicMethod("HelloWorld", // name of the method typeof(void), // return type new Type[] { }, // input types typeof(TestClass)); // class to 'attach the method to ILGenerator il = dm.GetILGenerator(); // create a constanst string using the // message and put it on the stack il.Emit(OpCodes.Ldstr, this.message); // invoke Console.WriteLine(string) // since it's a static method it only // requires a string (the one on the stack) il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); // return il.Emit(OpCodes.Ret); // create a delegate this.myTestMethod = (TestMethod) dm.CreateDelegate( typeof(TestMethod) ); } public void CreateMethod2() { /* * this is equivalent to: * Console.WriteLine(this.message); * there is access to object members * so the method cannot be declared as 'static' * */ // declare the method signature as // void HelloWorld(TestClass implicitThis) // it will be mapped to // TestClass.HelloWorld() as instance method // DynamicMethod dm = new DynamicMethod( "HelloWorld", // name of the method typeof(void), // return type new Type[] { typeof(TestClass) }, // input types typeof(TestClass)); // class to 'attach' the method to ILGenerator il = dm.GetILGenerator(); // puts the arg_o (this) on the stack il.Emit(OpCodes.Ldarg_0); // puts (POP)->message on the stack il.Emit(OpCodes.Ldfld, typeof(TestClass).GetField("message", BindingFlags.NonPublic | BindingFlags.Instance)); // invoke Console.WriteLine(string) // since it's a static method it only // requires a string (the one on the stack) il.Emit(OpCodes.Call, typeof(Console).GetMethod( "WriteLine", new Type[] { typeof(string) } ) ); // return il.Emit(OpCodes.Ret); // create a delegate this.myTestMethod = (TestMethod)dm.CreateDelegate(typeof(TestMethod), this); } } public class HelloWorld { public static void Main(string[] args) { TestClass myTestClass = new TestClass("Hello World!"); myTestClass.CreateMethod(); myTestClass.MyTestMethod(); myTestClass.CreateMethod2(); myTestClass.MyTestMethod(); } }
-quack