'Call a System.Action in a .NET dynamic method

I have a function that is building a dynamic method. As part of this dynamic method, it is calling an action known at generation time. As a minimal reproducible example, consider the following C# code:

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

When I run this, I get a System.InvalidProgramException. What is the correct way to do this?



Solution 1:[1]

Use that delegate

If you have a delegate and you will invoke it you can use it exactly instead of creating a new delegate. Just static Action MakeAction(Action action) => action.

Static or instance delegates

Delegates can be made of static or instance methods so you must make specific call for both.

Static methods calls is simple: no this argument, just call and ret.

For instance method calls you must load target object to stack and run callvirt (null-check and allows virtual methods). ILGenerator does not allow you to load some object from other memory. DynamicMethod allows you to create static method that will be instance delegate. For this you must add this argument and add owner class for method.

Final solution:

static Action MakeAction(Action action)
{
    // static method
    if (action.Method.IsStatic)
    {
        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>();
    }
    // instance method
    else
    {
        DynamicMethod method = new DynamicMethod("test", null, new[] { typeof(object) }, typeof(object));
        ILGenerator generator = method.GetILGenerator();
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Callvirt, action.Method);
        generator.Emit(OpCodes.Ret);
        return method.CreateDelegate<Action>(action.Target);
    }
}

System.Linq.Expressions

You can use Expressions for better runtime method building. It is much easier that DynamicMethod and Emit.

Creating delegate from delegate in Linq.Expressions:

static Action MakeAction(Action action)
{
    Expression target = action.Target == null ? null : Expression.Constant(action.Target);
    Expression body = Expression.Call(target, action.Method);
    Expression<Action> expr = Expression.Lambda<Action>(body);
    return expr.Compile();
}

Creating delegate from lambda-expression:

static Action MakeAction(Expression<Action> expr)
{
    return expr.Compile();
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1