c#DynamicMethod异常

c#DynamicMethod异常,c#,C#,我有以下代码 var dynamicAdd2 = new DynamicMethod("add", typeof(string), new[] { typeof(TestType) }, typeof(Program).Module, false); var add2Body = typeof(Program).GetMethod("add2").GetMethodBody().GetILAsByteArray(); var dynamicIlInfo

我有以下代码

       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
测试类型:

  public class TestType
  {
    public string Name = "test";
  }
我收到一个无效程序异常,没有更多信息
因此,我预计动态方法的创建将失败。我认为动态方法找不到TestClass的引用。或者在这种情况下会有什么问题?或者我该怎么做才能得到问题所在的提示?异常不会带来所需的信息…

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

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

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


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

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());
}
static void Main(字符串[]args)
{
var dynamicAdd2=新的DynamicMethod(“添加”,
类型(字符串),
新[]{typeof(Program),typeof(TestType)},
类型(程序)。模块,
假);
var ilGenerator=dynamicAdd2.GetILGenerator();
ilGenerator.DeclareLocal(字符串类型));
ilGenerator.Emit(操作码.Ldarg_1);
var fld=typeof(TestType).GetField(“名称”);
ilGenerator.Emit(操作码.Ldfld,fld);
ilGenerator.Emit(操作码.Ret);
var test2=(Func)dynamicAdd2.CreateDelegate(typeof(Func),new Program());
var ret2=test2(新的TestType());
}

解决方案2: 若您不能使用IlGenerator,并且需要使用GetDynamicILInfo直接操纵IL流,那个么您必须用对生成的动态方法有效的值替换IL流中的令牌。替换令牌通常需要知道这些令牌在IL流中的偏移量。问题是,精确的偏移量取决于编译器(对于发布/调试版本甚至是不同的)。因此,您要么使用一些IL disassembler来获取这些偏移量,要么编写能够实现这一点的IL解析器(这不是一件小事,也许您可以为此找到一些库)。所以下面的代码使用了一种“肮脏的黑客”使其在这种特殊情况下工作,但通常不起作用

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());
}
publicstaticvoidmain()
{
var dynamicAdd2=新的DynamicMethod(“添加”,
类型(字符串),
新[]{typeof(Program),typeof(TestType)},
类型(程序)。模块,
假);
var add2Body=typeof(Program).GetMethod(“add2”).GetMethodBody();
var add2ILStream=add2Body.GetILAsByteArray();
var dynamicIlInfo=dynamicAdd2.GetDynamicILInfo();
var token=dynamicIlInfo.gettoken(typeof(TestType).GetField(“Name”).FieldHandle);
var tokenBytes=BitConverter.GetBytes(令牌);
//这试图通过在IL流中搜索ldfld的操作码(0x7B)来查找ldfld使用的令牌的索引。
//令牌遵循这个指令,所以我添加+1
//在一般情况下不起作用,因为IL流可能在其他不相关的地方包含0x7B。
var tokenIndex=add2ILStream.ToList().IndexOf(0x7b)+1;
复制(tokenBytes,0,add2ILStream,tokenIndex,4)//
//从原始add2方法复制局部变量的签名
var localSignature=SignatureHelper.GetLocalVarSigHelper();
var localVarTypes=add2Body.LocalVariables.Select(=>wu.LocalType.ToArray();
AddArguments(localVarTypes,null,null);
dynamicIlInfo.SetLocalSignature(localSignature.GetSignature());
dynamicIlInfo.SetCode(add2ILStream,1);
var test2=(Func)dynamicAdd2.CreateDelegate(typeof(Func));
var ret2=test2(新的TestType());
}
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());
}