es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Llamar a un System.Action en un método dinámico de .NET.

Tengo una función que está construyendo un método dinámico. Como parte de este método dinámico, está llamando a una acción conocida en el momento de la generación. Como ejemplo mínimo y reproducible, considera el siguiente código C#:

using System.Reflection.Emit;

static class Program {
  static Action MakeAction(Action action) {
    DynamicMethod method = new DynamicMethod("test", null, null);
    ILGenerator generator = method.GetILGenerator();
    generator.Emit(OpCodes.Call, action.Method);
    generator.Emit(OpCodes.Ret);
    return method.CreateDelegate<action>();
  }

  static void Main() {
    MakeAction(() => Console.WriteLine("Hello"))();
  }
}

Cuando ejecuto esto, obtengo una System.InvalidProgramException. ¿Cuál es la forma correcta de hacer esto?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema con el código proporcionado es que el método MakeAction está intentando emitir una instrucción de llamada al método action.Method, que no es un método virtual y, por lo tanto, no puede ser llamado dinámicamente. Para solucionar esto, puedes usar la API Expression para crear un delegado a partir del parámetro action y luego emitir una instrucción de llamada al delegado creado en su lugar. Aquí tienes una versión actualizada del código:

    using System;
    using System.Linq.Expressions;
    using System.Reflection.Emit;
    
    static class Program {
      static Action MakeAction(Action action) {
        DynamicMethod method = new DynamicMethod("test", null, null);
        ILGenerator generator = method.GetILGenerator();
        var actionDelegate = Expression.Lambda<action>(Expression.Call(Expression.Constant(action.Target), action.GetMethodInfo())).Compile();
        generator.Emit(OpCodes.Call, actionDelegate.Method);
        generator.Emit(OpCodes.Ret);
        return method.CreateDelegate<action>();
      }
    
      static void Main() {
        MakeAction(() => Console.WriteLine("hello"))();
      }
    }
    

    Con este cambio, la salida debería ser “hello”, como se espera.

Comments are closed.