首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >c# DynamicMethod异常

c# DynamicMethod异常
EN

Stack Overflow用户
提问于 2017-02-05 16:30:57
回答 1查看 243关注 0票数 2

我有以下代码

代码语言:javascript
复制
       var dynamicAdd2 = new DynamicMethod("add",
typeof(string),
new[] { typeof(TestType) },
typeof(Program).Module, false);

      var add2Body = typeof(Program).GetMethod("add2").GetMethodBody().GetILAsByteArray();

      var dynamicIlInfo = dynamicAdd2.GetDynamicILInfo();
      var ilGenerator = dynamicAdd2.GetILGenerator();
      dynamicIlInfo.SetLocalSignature(SignatureHelper.GetLocalVarSigHelper().GetSignature());
      dynamicIlInfo.SetCode(add2Body, 1000);

      var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>));
     var ret2 = test2(new TestType()); // <-- Exception

add2:

代码语言:javascript
复制
public string add2(TestType digit)
{
  return digit.Name;
}

testType:

代码语言:javascript
复制
  public class TestType
  {
    public string Name = "test";
  }

我得到一个InvalidProgrammException,没有更多的信息,所以我希望动态方法的创建失败。我认为动态方法找不到对TestClass的引用。或者在这种情况下会有什么问题?或者我能做些什么才能得到问题所在的提示呢?例外不会带来所需的信息..。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-02-06 23:58:23

您不能直接将IL流从现有方法复制到动态方法,因为IL使用所谓的令牌(32位数字)来表示类型、方法或字段。对于同一字段,令牌的值在不同的模块中可能不同,因此字节复制方法IL流不替换令牌将导致程序无效。

第二个问题是,因为add2是实例方法(而不是静态的),所以必须添加该方法所属类型的实例作为方法的第一个参数。在C#中,实例方法的第一个参数是隐藏的,但是IL需要它。或者可以将方法声明为静态方法以避免这种情况。

第三个问题是add2方法包含(编译器生成的)局部变量。必须将此变量添加到本地签名(使用SetLocalSignature()方法),否则您的方法将使用未声明的变量。(请参阅下面的代码以了解如何做到这一点)。

解决方案1:

第一个解决方案是使用GetILGenerator()而不是GetDynamicILInfo(),从头重写IL流。您可以使用IL反汇编程序(例如ILDASM、.NET反射器)来获取任何现有方法的指令列表。使用IlGenerator (.)将这些指令写入IlGenerator.Emit应该不难。

代码语言:javascript
复制
static void Main(string[] args)
{
    var dynamicAdd2 = new DynamicMethod("add",
            typeof(string),
            new[] { typeof(Program), typeof(TestType) },
            typeof(Program).Module,
            false);

    var ilGenerator = dynamicAdd2.GetILGenerator();
    ilGenerator.DeclareLocal(typeof(string));
    ilGenerator.Emit(OpCodes.Ldarg_1);
    var fld = typeof(TestType).GetField("Name");
    ilGenerator.Emit(OpCodes.Ldfld, fld);
    ilGenerator.Emit(OpCodes.Ret);

    var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>), new Program());
    var ret2 = test2(new TestType());
}

解决方案2:

如果不能使用IlGenerator,并且需要使用GetDynamicILInfo直接操作IL流,则必须用对生成的动态方法有效的值替换IL流中的令牌。替换令牌通常要求您知道IL流中这些令牌的偏移量。问题是,精确的偏移取决于编译器(甚至对于版本/调试构建来说也是不同的)。所以,要么使用一些IL反汇编程序来获取这些偏移,要么编写能够这样做的IL解析器(这并不简单,也许您可以找到相应的库)。因此,下面的代码使用了某种“脏黑客”来使其在这种特殊情况下工作,但通常不起作用。

代码语言:javascript
复制
public static void Main()
{
    var dynamicAdd2 = new DynamicMethod("add",
        typeof(string),
        new[] { typeof(Program), typeof(TestType) },
        typeof(Program).Module,
        false);

    var add2Body = typeof(Program).GetMethod("add2").GetMethodBody();
    var add2ILStream = add2Body.GetILAsByteArray();

    var dynamicIlInfo = dynamicAdd2.GetDynamicILInfo();

    var token = dynamicIlInfo.GetTokenFor(typeof(TestType).GetField("Name").FieldHandle);
    var tokenBytes = BitConverter.GetBytes(token);
    //This tries to find index of token used by ldfld by searching for it's opcode (0x7B) in IL stream.
    //Token follows this instructions so I add +1. This works well for this simple method, but
    //will not work in general case, because IL stream could contain 0x7B on other unrelated places.
    var tokenIndex = add2ILStream.ToList().IndexOf(0x7b) + 1;
    Array.Copy(tokenBytes, 0, add2ILStream, tokenIndex, 4);//

    //Copy signature of local variables from original add2 method
    var localSignature = SignatureHelper.GetLocalVarSigHelper();
    var localVarTypes = add2Body.LocalVariables.Select(_ => _.LocalType).ToArray();
    localSignature.AddArguments(localVarTypes, null, null);
    dynamicIlInfo.SetLocalSignature(localSignature.GetSignature());

    dynamicIlInfo.SetCode(add2ILStream, 1);

    var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>));
    var ret2 = test2(new TestType());
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42054484

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档