2016-07-31 5 views
4

Ich habe versucht, den folgenden Code als IL-Code zur Laufzeit zu emittieren.InvalidProgramException (ungültiger IL-Code)?

class TestObject { 
     public int Hello {get;set;} 
     public int Test {get;set;} 
    } 

    static TestObject test(BinaryReader reader) { 
     var a = new TestObject(); 
     a.Hello = reader.ReadInt32(); 
     a.Test = reader.ReadInt32(); 
     return a; 
    } 

LINQPad zeigt:

test: 
IL_0000: nop   
IL_0001: newobj  UserQuery+TestObject..ctor 
IL_0006: stloc.0  // a 
IL_0007: ldloc.0  // a 
IL_0008: ldarg.0  
IL_0009: callvirt System.IO.BinaryReader.ReadInt32 
IL_000E: callvirt UserQuery+TestObject.set_Hello 
IL_0013: nop   
IL_0014: ldloc.0  // a 
IL_0015: ldarg.0  
IL_0016: callvirt System.IO.BinaryReader.ReadInt32 
IL_001B: callvirt UserQuery+TestObject.set_Test 
IL_0020: nop   
IL_0021: ldloc.0  // a 
IL_0022: stloc.1  
IL_0023: br.s  IL_0025 
IL_0025: ldloc.1  
IL_0026: ret   

Der Versuch, es mit C# zu reproduzieren:

 var method = new DynamicMethod("DynamicCreate", typeof(TestSubject), new Type[] {typeof(BinaryReader)}, 
      typeof(TestSubject).Module); 
     var il = method.GetILGenerator(); 

     var properties = from property in typeof(TestSubject).GetProperties() 
      let orderAttribute = 
       property.GetCustomAttributes(typeof(OrderAttribute), false).SingleOrDefault() as OrderAttribute 
      orderby orderAttribute.Order 
      select property; 

     il.Emit(OpCodes.Nop); 
     il.Emit(OpCodes.Newobj, typeof(TestSubject).GetConstructors()[0]); // pushes a new instance of testsubject on the stack 
     il.Emit(OpCodes.Stloc_0); // pop the instance to local variable 0 
     foreach (var prop in properties) 
     { 
      il.Emit(OpCodes.Ldloc_0); // load local variable 0 
      il.Emit(OpCodes.Ldarg_0); // load argument 0 (the binary reader) 
      il.Emit(OpCodes.Callvirt, typeof(BinaryReader).GetMethod("ReadInt32", BindingFlags.Public | BindingFlags.Instance)); // call the binary reader method (ReadInt32) 
      il.Emit(OpCodes.Callvirt, prop.SetMethod); // call the setter of the property (instance of local variable 0 and return value of readint32) 
      il.Emit(OpCodes.Nop); 
     } 
     il.Emit(OpCodes.Ldloc_0); 
     il.Emit(OpCodes.Stloc_1); 
     var label = il.DefineLabel(); 
     il.Emit(OpCodes.Br_S, label); 
     il.MarkLabel(label); 
     il.Emit(OpCodes.Ldloc_1); // push the test subject instance 
     il.Emit(OpCodes.Ret); // and return 

     var generator = (Load)method.CreateDelegate(typeof(Load)); 
     var reader = new BinaryReader(new MemoryStream(new byte[] {1, 2, 3, 4, 0, 0, 0, 1})); 
     var test = generator(reader); // exception here 

Die TestSubject Klasse:

public class TestSubject 
{ 

    [Order] 
    public int Test1 { get; set; } 

    [Order] 
    public int Test2 { get; set; } 

} 

mir die folgende Ausnahme geben:

System.InvalidProgramException { "Die Common Language Runtime hat ein ungültiges Programm gefunden."}

Was ist daran falsch?

Antwort

3

Sie müssen Einheimische deklarieren, bevor Sie sie verwenden. Außerdem kann der Code vereinfacht werden (die IL wurde in einem Debug-Build erstellt).

il.DeclareLocal(typeof(TestSubject)); 

il.Emit(OpCodes.Newobj, typeof(TestSubject).GetConstructors()[0]); // pushes a new instance of testsubject on the stack 
il.Emit(OpCodes.Stloc_0); // store the instance in local variable 0 
foreach (var prop in properties) 
{ 
    il.Emit(OpCodes.Ldloc_0); // load local variable 0 
    il.Emit(OpCodes.Ldarg_0); // load argument 0 (the binary reader) 
    il.Emit(OpCodes.Callvirt, typeof(BinaryReader).GetMethod("ReadInt32", BindingFlags.Public | BindingFlags.Instance)); // call the binary reader method (ReadInt32) 
    il.Emit(OpCodes.Callvirt, prop.SetMethod); // call the setter of the property (instance of local variable 0 and return value of readint32) 
} 
il.Emit(OpCodes.Ldloc_0); // push the test subject instance 
il.Emit(OpCodes.Ret); // and return 
+0

Dies scheint für mich arbeiten. Aber wie wir schon erwähnt haben, habe ich jetzt mit Expression-Bäumen gearbeitet. Und ich liebe es. Diese Frage hatte einen guten Lerneffekt für mich. – AmazingTurtle

1

Sie haben die Einheimischen nicht definiert.

Auch die IL ist hier aus Debug-Modus-Ausgabe. Es ist ein wenig komplizierter als nötig.

Reflexion emittieren ist fast veraltet (in einigen Fällen immer noch erforderlich). Es ist viel einfacher, einen solchen Code mit Hilfe von Ausdrucksbäumen zu erzeugen. Löschen Sie den gesamten Code und ersetzen Sie ihn durch Ausdrucksbäume.