2016-07-15 72 views
2

我试图在运行时生成一个新的类/对象。Reflection.Emit throws BadImageFormatException

在阅读How to create a private property using PropertyBuilder后,我设法实现了每一项操作,一切都像我需要的一样。

但只要我尝试实例化我的新目标,即时接收BadImageFormatException

这似乎是一个类似的问题,但没有解决Is there any way to instrument System.Reflection.Emit?

这里我的代码:

领域:

internal class Field { 
     public string FieldName; 
     public Type FieldType; 
     public string Value; 
    } 

发电机组代码:

var xx = new List<Field>(new[] { new Field { FieldName = "Name", FieldType = typeof(string), Value = "Hello World" }, 
     new Field { FieldName = "Id", FieldType = typeof(int), Value = "1" } }); 
     this.DoVodoo(xx); 

魔术

private dynamic DoVodoo(IEnumerable<Field> fields) { 
     var aName = new AssemblyName("DynamicAssemblyExample"); 
     var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave); 

     var mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll"); 

     // Create class with all needed Properties 
     var tb = mb.DefineType("ParamRow", TypeAttributes.Public, typeof(object)); 
     foreach (var field in fields) { 
     var pb = tb.DefineProperty(field.FieldName, PropertyAttributes.None, CallingConventions.HasThis, field.FieldType, null); 

     var getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; 
     // Define the "get" accessor method for the Property. 
     var custNameGetPropMthdBldr = tb.DefineMethod($"get_{field.FieldName}", getSetAttr, typeof(string), Type.EmptyTypes); 

     var custNameGetIL = custNameGetPropMthdBldr.GetILGenerator(); 

     custNameGetIL.Emit(OpCodes.Ldarg_0); 
     custNameGetIL.Emit(OpCodes.Ldfld, custNameGetPropMthdBldr); 
     custNameGetIL.Emit(OpCodes.Ret); 

     // Define the "set" accessor method for CustomerName. 
     var custNameSetPropMthdBldr = tb.DefineMethod($"set_{field.FieldName}", getSetAttr, null, new[] { typeof(string) }); 

     var custNameSetIL = custNameSetPropMthdBldr.GetILGenerator(); 

     custNameSetIL.Emit(OpCodes.Ldarg_0); 
     custNameSetIL.Emit(OpCodes.Ldarg_1); 
     //custNameSetIL.Emit(OpCodes.Stfld, custNameGetPropMthdBldr); 
     custNameSetIL.Emit(OpCodes.Stfld, custNameSetPropMthdBldr); 
     custNameSetIL.Emit(OpCodes.Ret); 

     // Last, we must map the two methods created above to our PropertyBuilder to 
     // their corresponding behaviors, "get" and "set" respectively. 
     pb.SetGetMethod(custNameGetPropMthdBldr); 
     pb.SetSetMethod(custNameSetPropMthdBldr); 
     } 

     var finalType = tb.CreateType(); 

     var result = new List<object>(); 

     foreach (var field in fields) { 
     var inst = ab.CreateInstance(finalType.Name); 
     finalType.GetProperty(field.FieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SetValue(inst, field.Value); //<-- Here comes the trouble 
     result.Add(inst); 
     } 
     return result;} 

任何帮助表示赞赏如何实例化我的新创建的类型ParamRow

Bonus-Question: 为什么会有BadImageFormatException

附加信息:

  • 的.Net框架4.6.1
  • 编译器的目标是86
  • 从此再也Reflection.Emit
+1

我即将有一个调试器看一下,看能不能找到这个错误,但是:如果你想避免他们和获得良好的错误信息的好方法,尝试[印记](HTTPS://www.nuget。组织/包/印记/) - 它是围绕IL发出(但在概念上相同)的包装,并且被设计成使它很难失败(或者至少,很容易弄清楚它为什么失败) –

回答

3

你得到的是异常的.Message重要的位:

字段标记超出范围。

这就告诉你,这是不理解你想要的领域ldfld/stfld使用 - 这是因为你给它传递方法令牌(custNameGetPropMthdBldr/custNameSetPropMthdBldr),而不是令牌。

您需要定义和使用领域:

var fb = tb.DefineField("__" + field.FieldName, field.FieldType, FieldAttributes.Private); 
// ... 
custNameGetIL.Emit(OpCodes.Ldarg_0); 
custNameGetIL.Emit(OpCodes.Ldfld, fb); 
custNameGetIL.Emit(OpCodes.Ret); 
// ... 
custNameSetIL.Emit(OpCodes.Ldarg_0); 
custNameSetIL.Emit(OpCodes.Ldarg_1); 
custNameSetIL.Emit(OpCodes.Stfld, fb); 
custNameSetIL.Emit(OpCodes.Ret); 

还请注意,这是更有效的通过反射创建对象时,使用Type;这工作得很好:

var inst = Activator.CreateInstance(finalType); 
+0

你,先生,是一个救生员。有点尴尬对我来说,我已经错过了backingfield * -.- – lokusking

+0

@lokusking我似乎记得,印记会显示在编译时这个错误;当调用方法来发出字段访问时,方法签名**要求**将其传递给字段对象。这是一个让IL更容易正确使用的好库。 –

+0

通常我会试试看,但公司政策使得从Nuget获取内容变得非常困难。我会在家尝试 – lokusking