2016-07-15 104 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