C# 从操作创建DynamicMethod<;T>;说明书

C# 从操作创建DynamicMethod<;T>;说明书,c#,reflection,dynamicmethod,C#,Reflection,Dynamicmethod,我正在使用DynamicMethod,目标是执行以下操作: 我有一个操作,使用GetILAsByteArray()从中获取IL代码作为字节。我想从这个字节创建一个动态方法并执行is。下面是我尝试做的一个例子: class Program { static void Main(string[] args) { //Create action and execute Action<string> myAction = s =>

我正在使用DynamicMethod,目标是执行以下操作:

我有一个操作,使用
GetILAsByteArray()
从中获取IL代码作为字节。我想从这个字节创建一个动态方法并执行is。下面是我尝试做的一个例子:

class Program
{
    static void Main(string[] args)
    {
        //Create action and execute
        Action<string> myAction = s =>
        {
            Console.WriteLine("Hello " + s);
        };
        myAction("World");
        //Get IL bytes
        byte[] ilBytes = myAction.GetMethodInfo().GetMethodBody().GetILAsByteArray();
        DynamicMethod dynamicCallback = new DynamicMethod("myAction", typeof(void), new Type[] { typeof(string) });
        DynamicILInfo dynamicIlInfo = dynamicCallback.GetDynamicILInfo();
        dynamicIlInfo.SetCode(ilBytes, 100);
        dynamicCallback.Invoke(null, new object[] { "World" });
    }
}

注:DynamicMethodHelper是由罗海波开发的类,在a中有描述,但也可以直接下载。

您可以这样做:

byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
dm.Invoke(myAction.Target, new object[] { myAction.Target, "World" });
ILReader
是一个为您完成艰苦工作的课程。你可以从中复制它

例如:

MethodInfo method = ...
DynamicMethod dm = new DynamicMethod(
     method.Name,
     method.ReturnType,
     method.GetParameters.Select(pi => pi.ParameterType).ToArray(),
     method.DeclaringType,
     skipVisibility: true\fasle - depends of your need);

DynamicILInfo ilInfo = dm.GetDynamicILInfo();
var body = method.GetMethodBody();
SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper();
foreach(LocalVariableInfo lvi in body.LocalVariables)
{
    sig.AddArgument(lvi.LocalType, lvi.IsPinned);
}
ilInfo.SetLocalSignature(sig.GetSignature());
byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
如果您的方法是一个简单的方法(不是泛型的,也没有异常句柄),那么thid应该可以工作

如果您的方法是泛型方法,则需要执行以下操作以将所有者类型传递给DynamicMethod构造函数:

var owner = method.DeclaringType.MakeGenericType(
             method.DeclaringType.GetGenericArguments());
还有一件事,如果它仍然不起作用,并且您的方法是实例方法,请在
DynamicMethod
构造函数的parameters数组的第一个单元格中传递该方法的instacne类型

更新

您不能在此处传递
null
dm.Invoke(**null**,新对象[]{“世界”})myAction不是静态方法

myAction
Action
)实际上是一个新生成的类中的方法,该类保存该方法

但我检查了,即使我传递了
myAction.Target
或该类型的新实例,也会引发异常。异常(CLR DEDEECT无效程序)告诉您IL不完全正确。我现在不能告诉你到底是什么问题,但如果这对你很重要,我可以在下周回去工作时检查一下

无论如何,如果您只想看到DynamicIlInfo.SetCode的运行,您可以按原样使用代码,但只需从以下内容更改方法信息:

class Program
{        
    static void Main(string[] args)
    {
        Action<string> myAction = s =>
        {
            Console.WriteLine("Hello " + s);
        };
        MethodInfo method = myAction.GetMethodInfo();

        //Rest of your code
    }
}
更新2:

显然我昨天很累,我没有意识到你的错误

正如我在最初的回答中所写

还有一件事,如果它仍然不起作用,并且您的方法是实例方法,请在
DynamicMethod
构造函数的parameters数组的第一个单元格中传递该方法的instacne类型

因此,您需要这样做:

DynamicMethod dm = new DynamicMethod(
     method.Name,
     method.ReturnType,
     new[] {method.DeclaringType}.
        Concat(method.GetParameters().
        Select(pi => pi.ParameterType)).ToArray(),
     method.DeclaringType,
     skipVisibility: true);
然后像这样调用动态方法:

byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
dm.Invoke(myAction.Target, new object[] { myAction.Target, "World" });

现在,它的工作非常完美。

我认为使用反射无法获得maxStackSize值。但事实上,这不是问题所在。问题是
Console.WriteLine
调用被编码为元数据标记(MethodRef可能),元数据标记仅在声明它的模块的范围内有效。查看
DynamicILInfo.getToken有关
函数,这些函数将导入其他元数据项并创建对
DynamicMethod
有效的令牌@thehenny我尝试过但没有成功,请参阅我的编辑。您必须用
getToken
方法返回给您的新创建的标记替换IL字节数组中的旧标记。我认为我编写的代码已经没有意义,我没有立即获取它。谢谢。有没有一个通用的方法可以做到这一点,而不需要知道在行动中被称为什么?例如,是否有一种方法可以在最后动态地实现这一点?是的,您可以解析方法体字节数组,然后使用
模块解析所有令牌。Resolvexxx
方法。声明的
ILInfoGetTokenVisitor
在哪里?@Sjoerd222888,并且有一篇关于它的博文,我立即得到一个“System.Reflection.TargetInvocationException”,消息为:“{”错误的二进制签名。(来自HRESULT:0x80131192的异常)“}”在尝试调用动态方法时。我想我错过了一些基本信息。请查看我的更新。我仍然得到一个
TargetInvocationException
。我哪里出错了?@Sjoerd222888立即检查它