DynamicMethodのメモ

CLR Method Dispatch Internals: From Static to Dynamicのデモコードの写し。コード確認するのにデモ見るのはしんどいのでメモっておきます。(^^;

using System;
using System.Reflection;
using System.Reflection.Emit;

delegate object DynamicMethodDelegate (params object args);

class Foo {
    public void m <T> () {
        Console.WriteLine (typeof (T));
    }
}

static class Program {
    static int methodCounter = 0;

    public static DynamicMethod DynamicMethodFactory (MethodInfo mi) {
        // 引数がobjectで戻り値がobjectのメソッドを定義
        DynamicMethod dm = new DynamicMethod ("tempMethod" + methodCounter++, 
                typeof (object), new Type  { typeof (object ) }, mi.DeclaringType);
        ILGenerator il = dm.GetILGenerator ();

        ParameterInfo  parameters = mi.GetParameters ();

        // インスタンスメソッドの場合、第一引数にインスタンスオブジェクトを取る
        if (!mi.IsStatic) {
            il.Emit (OpCodes.Ldarg_0);
            il.Emit (OpCodes.Ldc_I4_0);
            il.Emit (OpCodes.Ldelem_Ref);
            il.Emit (OpCodes.Castclass, mi.DeclaringType);
        }

        for (int i = 0; i < parameters.Length; ++i) {
            il.Emit (OpCodes.Ldarg_0);
            il.Emit (OpCodes.Ldc_I4, i + (mi.IsStatic ? 0 : 1));
            il.Emit (OpCodes.Ldelem_Ref);
            il.Emit (OpCodes.Castclass, parameters [i].ParameterType);
        }

        il.Emit (mi.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi);
        // 呼び出しているメソッドの戻り値がvoidなら、取りあえずnullを返す
        if (mi.ReturnParameter.ParameterType == typeof (void))
            il.Emit (OpCodes.Ldnull);

        il.Emit (OpCodes.Ret);

        return dm;
    }

    public static void Main (string args) {
        Foo o = new Foo ();
        MethodInfo mi = o.GetType ().GetMethod ("m", 
                BindingFlags.Instance | BindingFlags.Static | 
                BindingFlags.Public | BindingFlags.NonPublic);
        // GenericパラメータにProgram型を指定
        MethodInfo gmi = mi.MakeGenericMethod (typeof (Program));
        DynamicMethod dm = DynamicMethodFactory (gmi);
        // Delegate経由で呼び出し
        DynamicMethodDelegate d = (DynamicMethodDelegate)dm.CreateDelegate (typeof (DynamicMethodDelegate));
        d (o);
    }
}

自分が必要な部分だけになっているのと、一部改変しているので注意。