我有以下代码
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()); // <-- Exceptionadd2:
public string add2(TestType digit)
{
return digit.Name;
}testType:
public class TestType
{
public string Name = "test";
}我得到一个InvalidProgrammException,没有更多的信息,所以我希望动态方法的创建失败。我认为动态方法找不到对TestClass的引用。或者在这种情况下会有什么问题?或者我能做些什么才能得到问题所在的提示呢?例外不会带来所需的信息..。
发布于 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应该不难。
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解析器(这并不简单,也许您可以找到相应的库)。因此,下面的代码使用了某种“脏黑客”来使其在这种特殊情况下工作,但通常不起作用。
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());
}https://stackoverflow.com/questions/42054484
复制相似问题