2016-07-15 6 views
2

Ich versuche, eine neue Klasse/Objekt zur Laufzeit zu generieren.Reflection.Emit wirft BadImageFormatException

Nachdem ich How to create a private property using PropertyBuilder gelesen habe, habe ich es geschafft alles zu implementieren und alles ist wie ich es brauche.

Aber sobald im Versuch, mein neues Objekt instanziieren, im ein BadImageFormatException

Empfang Dies scheint ein ähnliches Problem zu sein, aber ungelöst Is there any way to instrument System.Reflection.Emit?

Hier meinen Code:

Field:

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

Generator-Code:

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); 

The Magic

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;} 

Jede Hilfe ist willkommen, wie meine neu erstellte Typ ParamRow instanziieren.

Bonus-Frage: Warum gibt es eine BadImageFormatException?

Zusätzliche Information:

  • .Net-Framework-4.6.1
  • Compiler-Ziel ist x86
  • Nie hat jemals Reflection.Emit vor
+1

Ich bin über einen Blick auf sie in einem Debugger haben, und sehen, ob ich den Fehler erkennen kann, aber: wenn Sie eine gute Art und Weise wollen sie zu vermeiden und eine gute Fehlermeldungen bekommen, versuchen [Siegel] (https: //www.nuget.org/packages/Sigil /) - es ist ein Wrapper um IL emittieren (aber konzeptionell das gleiche), und ist so konzipiert, dass es schwer zu scheitern (oder zumindest, einfach herauszufinden, warum es gescheitert ist) –

Antwort

3

Die .Message der Ausnahme, die Sie erhalten, ist das wichtige Bit:

Feldmarke außerhalb des Bereichs.

Dies sagt Ihnen, dass es nicht zu verstehen, was Feld, das Sie in ldfld/stfld verwenden möchten - das ist, weil Sie es das Methode Token (custNameGetPropMthdBldr/custNameSetPropMthdBldr) anstelle eines Feld vorbei sind Zeichen.

Sie benötigen ein Feld zu definieren und zu verwenden:

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); 

Beachten Sie auch, dass es effizienter ist, die Type als die Namen zu verwenden, wenn Objekte durch Reflexion zu schaffen; das funktioniert gut:

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

Sie, Sir, sind ein Lebensretter. Etwas peinlich für mich, dass ich das Backing-Feld verpasst habe * -.- – lokusking

+0

@lokusking Ich erinnere mich, dass Sigil diesen Fehler zur Kompilierzeit gezeigt hätte; Beim Aufrufen der Methode zum Senden von Feldzugriff fordert ** die Methodensignatur **, dass Sie ein Feldobjekt übergeben. Es ist eine großartige Bibliothek, um IL leichter zu machen. –

+0

Normalerweise würde ich es versuchen, aber Company-Policy macht es wirklich schwer, Sachen von Nuget zu bekommen. Ich werde es zu Hause versuchen – lokusking

Verwandte Themen