Pregunta

He estado mirando Reflection.Emit recientemente. Escribí un programa simple que genera un DynamicMethod que simplemente llama a otro método con los mismos parámetros

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);
    }
}

Cuando ejecuto este ejemplo, esperaría que produjera 1,2,3,4,5,6, sin embargo, genera 2,3,4,5,6,1

No estoy muy seguro de por qué ... Si ustedes saben de algún buen recurso para usar Reflection.Emit, ¿podrían señalarme en esa dirección? He estado usando Reflector con el complemento Emit.

Saludos

Rohan

¿Fue útil?

Solución

El problema que tiene es que está invocando un método dinámico, no estático. Su método generado no tiene una referencia a la instancia de la clase Program.

También tenga en cuenta que está introduciendo 7 parámetros en la pila para un método de 6 parámetros. El primer parámetro debe ser una referencia al objeto sobre el que está invocando el método.

El comportamiento extraño que está viendo puede deberse a que no hay un parámetro del índice 6, y que se remonta al inicio de la matriz de parámetros.

Consulte " Cómo: Definir y ejecutar métodos dinámicos " en la ayuda de VS.

Puede hacerlo funcionar aceptando un parámetro de objeto en su llamada al método o haciéndolo estático:

público delegado void TestHandler (instancia de objeto, 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);
}

Otros consejos

Para que funcione, deberías

  1. convierte la Pregunta en un método estático
  2. comentario gen.Emit(OpCodes.Ldarg_S,6);
  3. modificar MethodInfo method1 = ... en consecuencia

Uno " olor " es que al detener la depuración en el método Question no se puede evaluar la referencia this . Y esto no debería ser para un método de instancia ... ;-)

Editar: Ops. Acabo de ver la respuesta de Robert Wagner, que está mucho mejor explicada que la mía. Listo para cancelar mi publicación si es necesario ... :-)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top