【问题标题】:Call a System.Action in a .NET dynamic method在 .NET 动态方法中调用 System.Action
【发布时间】:2022-05-13 19:02:22
【问题描述】:

我有一个正在构建动态方法的函数。作为此动态方法的一部分,它正在调用生成时已知的操作。作为一个最小的可重现示例,请考虑以下 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"))();
  }
}

当我运行它时,我得到一个 System.InvalidProgramException。这样做的正确方法是什么?

【问题讨论】:

  • 你为什么要尝试使用反射来找出委托所指向的方法并直接调用它而不是仅仅调用委托?它似乎不必要地复杂且容易出错,并且给您提供了比您需要的更多的思考方式。如果您确实只想执行由methdoinfo定义的任意方法,为什么要接受委托而不是直接接受方法信息?
  • 直接传递MethodInfo是个好主意,不知道为什么我没有想到。传递给它的 Action 要么来自另一个 DynamicMethod(可以作为 MethodInfo 传递),要么是一个 lambda。将 lambdas 更改为静态方法并使用该静态方法的 MethodInfo 是可行的,尽管它不太干净。

标签: c# cil dynamicmethod ilgenerator


【解决方案1】:

使用该委托

如果您有一个委托并且您将调用它,您可以完全使用它而不是创建一个新委托。只需static Action MakeAction(Action action) =&gt; action

静态或实例委托

委托可以由静态方法或实例方法组成,因此您必须对两者进行特定调用。

静态方法调用很简单:没有this 参数,只有callret

对于实例方法调用,您必须将目标对象加载到堆栈并运行callvirt(空检查并允许虚拟方法)。 ILGenerator 不允许您从其他内存加载某些对象。 DynamicMethod allows 您创建将作为实例委托的静态方法。为此,您必须添加 this 参数并为方法添加 owner 类。

最终解决方案:

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

您可以使用Expressions 来构建更好的运行时方法。 DynamicMethod 和 Emit 要容易得多。

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

从 lambda 表达式创建委托:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-18
    • 2010-09-29
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    相关资源
    最近更新 更多