'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 |