Странная последовательность параметров с использованием Reflection.Emit - PullRequest
1 голос
/ 15 января 2009

Я давно смотрю на Reflection.Emit. Я написал простую программу, которая генерирует DynamicMethod, который просто вызывает другой метод с теми же параметрами

class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Test();
    }

    public delegate void TestHandler(int a, int b, int c, int d, int e, int f);

    public void Test()
    {
        DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));


        MethodInfo method1 = typeof(Program).GetMethod("Question",BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,null,new Type[]{typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32)},null);
        MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
        MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);

        ILGenerator gen = method.GetILGenerator();

        gen.Emit(OpCodes.Nop);
        gen.Emit(OpCodes.Ldarg_S, 0);
        gen.Emit(OpCodes.Ldarg_S, 1);
        gen.Emit(OpCodes.Ldarg_S, 2);
        gen.Emit(OpCodes.Ldarg_S, 3);
        gen.Emit(OpCodes.Ldarg_S, 4);
        gen.Emit(OpCodes.Ldarg_S, 5);
        gen.Emit(OpCodes.Ldarg_S, 6);
        gen.Emit(OpCodes.Call, method1);
        gen.Emit(OpCodes.Nop);
        gen.Emit(OpCodes.Call, method2);
        gen.Emit(OpCodes.Call, method3);
        gen.Emit(OpCodes.Nop);
        gen.Emit(OpCodes.Ret);

        TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
        handler(1, 2, 3, 4, 5, 6);
    }

    public void Question(int a, int b, int c, int d, int e, int f)
    {
        Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
    }
}

Когда я запускаю этот пример, я ожидаю, что он выведет 1,2,3,4,5,6, однако он выдаст 2,3,4,5,6,1

Я не совсем уверен, почему ... Если вы, ребята, знаете о каких-либо хороших ресурсах для использования Reflection.Emit, не могли бы вы указать мне в этом направлении. Я использую Reflector с Emit AddIn.

Приветствия

Rohan

Ответы [ 2 ]

5 голосов
/ 15 января 2009

Проблема в том, что вы вызываете динамический метод, а не статический. Ваш сгенерированный метод не имеет ссылки на экземпляр класса Program.

Также обратите внимание, что вы помещаете 7 параметров в стек для метода 6 параметров. Первый параметр должен быть ссылкой на объект, для которого вы вызываете метод.

Странное поведение, которое вы видите, может быть связано с отсутствием параметра с индексом 6, и оно возвращается к началу массива параметров.

См. «Как: определить и выполнить динамические методы» в справке VS.

Вы можете заставить его работать, приняв параметр объекта в вызове вашего метода или сделав его статическим:

открытый делегат void TestHandler (экземпляр объекта, int a, int b, int c, int d, int e, int f);

public void Test()
{
    DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(object), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));


    MethodInfo method1 = typeof(Program).GetMethod("Question", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, null);
    MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
    MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);

    ILGenerator gen = method.GetILGenerator();

    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ldarg_S, 0);
    gen.Emit(OpCodes.Ldarg_S, 1);
    gen.Emit(OpCodes.Ldarg_S, 2);
    gen.Emit(OpCodes.Ldarg_S, 3);
    gen.Emit(OpCodes.Ldarg_S, 4);
    gen.Emit(OpCodes.Ldarg_S, 5);
    gen.Emit(OpCodes.Ldarg_S, 6);
    gen.Emit(OpCodes.Call, method1);
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Call, method2);
    gen.Emit(OpCodes.Call, method3);
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ret);

    TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
    handler(this, 1, 2, 3, 4, 5, 6);
}

public void Question(int a, int b, int c, int d, int e, int f)
{
    Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
}
1 голос
/ 15 января 2009

Чтобы это заработало, надо

  1. make Question a static method
  2. комментарий gen.Emit(OpCodes.Ldarg_S,6);
  3. изменить MethodInfo method1 = ... соответственно

Один "запах" заключается в том, что при отладке по методу Question вы не можете оценить ссылку this. И это не должно быть для метода экземпляра ...; -)

Редактировать: Ops. Я только что видел ответ Роберта Вагнера, который гораздо лучше объяснен, чем мой. Готов отменить мой пост, если это необходимо ...: -)

...