2016-06-29 18 views
2

Im Folgenden Code mein C#:Objekte hinzufügen in Schleife in IL Emit zur Liste - erkannt Common Language Runtime ein ungültiges Programm

List<int> list = new List<int>(); 
for(int Count = 0; Count < 5; Count++) 
    list.Add(Count); 
return list; 

Mein entsprechenden ausgesendeten Code wird wie folgt:

LocalBuilder list = ILout.DeclareLocal(typeof(List<int>)); 
LocalBuilder Count = ILout.DeclareLocal(typeof(int)); 
LocalBuilder CmpRes = ILout.DeclareLocal(typeof(bool)); 
ConstructorInfo DictConstrctor = typeof(List<int>).GetConstructor(new Type[] { }); 
MethodInfo methodinfo_add = typeof(List<int>).GetMethod("Add", new[] { typeof(int) }); 
Label IL_001C = ILout.DefineLabel(); 
Label IL_000B = ILout.DefineLabel(); 

ILout.Emit(OpCodes.Newobj, DictConstrctor); 
ILout.Emit(OpCodes.Stloc_0, list); 
ILout.Emit(OpCodes.Ldc_I4_0); 
ILout.Emit(OpCodes.Stloc_1, Count); 

ILout.Emit(OpCodes.Br_S, IL_001C); 
ILout.MarkLabel(IL_000B); 
ILout.Emit(OpCodes.Ldloc_0, list); 
ILout.Emit(OpCodes.Ldloc_1, Count); 
ILout.Emit(OpCodes.Call, methodinfo_add); 

ILout.Emit(OpCodes.Ldloc_1, Count); 
ILout.Emit(OpCodes.Ldc_I4_1); 
ILout.Emit(OpCodes.Add); 

ILout.Emit(OpCodes.Stloc_1, Count); 
ILout.MarkLabel(IL_001C); 
ILout.Emit(OpCodes.Ldloc_1, Count); 
ILout.Emit(OpCodes.Ldc_I4_2); 
ILout.Emit(OpCodes.Clt); 
ILout.Emit(OpCodes.Stloc_3, CmpRes); 
ILout.Emit(OpCodes.Ldloc_3, CmpRes); 
ILout.Emit(OpCodes.Brtrue_S, IL_000B); 

ILout.Emit(OpCodes.Ldloc_0, list); 
ILout.Emit(OpCodes.Ret); 

Es gibt eine Ausnahme aus - "Common Language Runtime hat ein ungültiges Programm erkannt.".

Was mache ich hier falsch? Jede Hilfe wird geschätzt.

+0

Das sieht aus wie es Loops zu 2 ... ist das absichtlich? –

+0

Wenn du mit emit spielen willst, empfehle ich dringend die Sigil-Bibliothek (https://www.nuget.org/packages/Sigil/) - sie ist ** entworfen **, um dies mit einer API viel intuitiver zu machen das macht es schwer zu versagen und gibt eine klare Fehlermeldung, wenn Sie es immer noch verwalten. –

+0

Danke! Ich werde es später versuchen. Ja, zählen ist 2. Ich habe zu viele Dinge versucht, also könnte ich die Anzahl auf 5 geändert haben .. – badari

Antwort

5
ILout.Emit(OpCodes.Stloc_1, Count); 

und

ILout.Emit(OpCodes.Ldloc_1, Count); 

machen keinen Sinn. Kein zusätzlicher Parameter wird benötigt, wenn Sie explizit sagen Ebenso

„1 lokal verwenden“: offen

ILout.Emit(OpCodes.Stloc_3, CmpRes); 
ILout.Emit(OpCodes.Ldloc_3, CmpRes); 

obwohl ich nicht sicher bin, dass CmpRes alle nützlichen Zweck dient; kein Punkt Speichern und Laden - lass es einfach auf dem Stack

Hinweis: Wenn Count „local 1“ ist, dann CmpRes ist „local 2“; Es gibt keine "lokale 3", daher sind Stloc_3 und Ldloc_3 fehlerhaft.

Und wieder hier:

ILout.Emit(OpCodes.Ldloc_0, list); 
ILout.Emit(OpCodes.Ldloc_1, Count); 

-

Als nächstes werden wir auf den Anruf bekommen; Sie machen einen statischen Anruf:

ILout.Emit(OpCodes.Call, methodinfo_add); 

aber das ist eine Instanzmethode für ein Objekt, sollte also ein virtueller Anruf sein.

und wieder eine andere lokale fingern hier:

ILout.Emit(OpCodes.Ldloc_1, Count); 

und hier:

ILout.Emit(OpCodes.Stloc_1, Count); 

und hier:

ILout.Emit(OpCodes.Ldloc_0, list); 

Ich habe jedoch auch erhebliche Zweifel, dass diese Schleife (auch wenn behoben) tut, was Sie erwarten, es zu tun - wenn ich es richtig gelesen habe, ist es tatsächlich:

var list = new List<int>(); 
for(int i [= 0] ; i < 2 ; i++) // note the 0 here implicit not explicit 
{ 
    list.Add(i); 
} 
return list; 
+0

"aber das ist eine ** Instanz ** Methode für ein Objekt, sollte also ein ** virtueller ** Aufruf sein". Obwohl es klar ist, dass Sie es wissen, kann der Satz andere irreführen. Instanz-Methode bedeutet nicht unbedingt eine virtuelle Methode ... Er muss den callvirt-Opcode verwenden, weil ** jede ** Instanz-Methode ihn für die 'this! = Null'-Prüfung verwendet. –

+0

@DudiKeleti obwohl allgemeiner, es ist einfach die richtige Anweisung für das Szenario; Alle gängigen Compiler werden callvirt für Objektmethoden ausgeben, auch wenn sie nicht virtuell sind (lassen Sie die JIT sich Gedanken um den Unterschied machen). Es gibt eine dritte Kategorie, "Constrained Call", für komplexere Szenarien (Generics, Interface-Methoden auf Strukturen, etc.). Alles Spaß. –

+0

ja sicher. Wenn wir jedoch genauer sind, in diesem Fall zum Beispiel "new MyClass.Method()", sieht die IL wie folgt aus: 'rufe Instanz void Namespace.MyClass :: Method()' auf. aber genug, um dich zu stören;) –