从操作指令创建动态方法<T>



我正在使用DynamicMethod,目标是做以下事情:

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

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

当调用dynamicCallback.Invoke(null, new object[] { "World" })时,我们得到"Exception thrown: 'System。BadImageFormatException' in mscorlib.dll".

我不知道的一件事是我应该用什么作为SetCode()的第二个参数,应该用什么作为'maxStackSize'?如何设置与初始操作相同的值?但我想这并不是例外的原因。

如何正确地从IL字节创建一个动态方法?


<

解决方案/strong>

这里我想总结一下Dudi Keleti提供的完整解决方案:

static void Main(string[] args)
{
    Action<string> myAction = s =>
    {
        Console.WriteLine("Hello " + s);
    };
    MethodInfo method = myAction.GetMethodInfo();
    object target = myAction.Target;
    DynamicMethod dm = new DynamicMethod(
        method.Name,
        method.ReturnType,
        new[] {method.DeclaringType}.
            Concat(method.GetParameters().
                Select(pi => pi.ParameterType)).ToArray(),
        method.DeclaringType,
        skipVisibility: true);
    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);
    DynamicMethodHelper.ILInfoGetTokenVisitor visitor = new DynamicMethodHelper.ILInfoGetTokenVisitor(ilInfo, code);
    reader.Accept(visitor);
    ilInfo.SetCode(code, body.MaxStackSize);
    dm.Invoke(target, new object[] { target, "World" });
    Console.ReadLine(); //Just to see the result
}

注意:DynamicMethodHelper是由Haibo Luo开发的类,在一篇博客文章中有描述,但也可以直接在这里下载

你可以这样做:

byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);

ILReader是一个为你做艰苦工作的班。你可以从这里复制。

的例子:

MethodInfo method = ...
DynamicMethod dm = new DynamicMethod(
     method.Name,
     method.ReturnType,
     method.GetParameters.Select(pi => pi.ParameterType).ToArray(),
     method.DeclaringType,
     skipVisibility: truefasle - 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);

如果你的方法是一个简单的方法(不是泛型的,没有异常句柄),第三步应该可以工作。

如果你的方法是泛型的,你需要这样做来传递所有者类型给DynamicMethod构造函数:

var owner = method.DeclaringType.MakeGenericType(
             method.DeclaringType.GetGenericArguments());

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

你不能在dm.Invoke(**null**, new object[] { "World" });这里传递null,因为myAction不是静态方法。

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

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

不管怎样,如果你只想看DynamicIlInfo。SetCode在操作中,你可以使用你的代码,但只需改变方法信息从this:

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

:

class Program
{
    static void M(string s)
    {
        Console.WriteLine("Hello " + s);
    }
    static void Main(string[] args)
    {
        MethodInfo method = typeof (Program).GetMethod("M", BindingFlags.Static | BindingFlags.NonPublic);
        //Rest of your code
    }
}
更新2:

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

正如我在原来的回答中所写的,

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

所以你需要这样做:

DynamicMethod dm = new DynamicMethod(
     method.Name,
     method.ReturnType,
     new[] {method.DeclaringType}.
        Concat(method.GetParameters().
        Select(pi => pi.ParameterType)).ToArray(),
     method.DeclaringType,
     skipVisibility: true);

并像这样调用动态方法:

dm.Invoke(myAction.Target, new object[] { myAction.Target, "World" });

相关内容

  • 没有找到相关文章

最新更新