Frage

In Microsoft IL, ein Verfahren auf einen Werttyp rufen Sie einen indirekten Verweis benötigen. Können sagen, wir haben ein ILGenerator „il“ genannt, und dass zur Zeit haben wir eine Nullable oben auf dem Stapel, wenn wir überprüfen wollen, ob es einen Wert hat, dann könnten wir die folgende emittieren:

var local = il.DeclareLocal(typeof(Nullable<int>));
il.Emit(OpCodes.Stloc, local);
il.Emit(OpCodes.Ldloca, local);
var method = typeof(Nullable<int>).GetMethod("get_HasValue");
il.EmitCall(OpCodes.Call, method, null);

Allerdings wäre es schön, überspringen sie als lokale Variable zu speichern, und einfach die Methode aufrufen, auf die Adresse der Variablen bereits auf dem Stapel, so etwas wie:

il.Emit(/* not sure */);
var method = typeof(Nullable<int>).GetMethod("get_HasValue");
il.EmitCall(OpCodes.Call, method, null);

Die ldind Familie von Befehlen sieht vielversprechend aus (besonders ldind_ref), aber ich kann nicht ausreichend Dokumentation zu wissen, ob diese Boxen des Wertes verursachen würde, was ich vermute, es könnte.

Ich habe einen Blick auf der C # Compiler ausgegeben habe, aber es nutzt lokale Variablen, dies zu erreichen, was mir den erste Weg glauben macht den einzige Weg sein kann. Wer noch keine besseren Ideen?

**** Edit: Zusätzliche Hinweise ****

Der Versuch, die Methode direkt aufzurufen, wie im folgende Programm mit den kommentierten Zeilen aus, funktioniert nicht (der Fehler wird „Operation die Laufzeit destabilisieren könnte“). Kommentar- der Zeilen, und Sie werden sehen, dass es wie erwartet funktioniert, Rückkehr „True“.

var m = new DynamicMethod("M", typeof(bool), Type.EmptyTypes);
var il = m.GetILGenerator();
var ctor = typeof(Nullable<int>).GetConstructor(new[] { typeof(int) });
il.Emit(OpCodes.Ldc_I4_6);
il.Emit(OpCodes.Newobj, ctor);
//var local = il.DeclareLocal(typeof(Nullable<int>));
//il.Emit(OpCodes.Stloc, local);
//il.Emit(OpCodes.Ldloca, local);
var getValue = typeof(Nullable<int>).GetMethod("get_HasValue");
il.Emit(OpCodes.Call, getValue);
il.Emit(OpCodes.Ret);
Console.WriteLine(m.Invoke(null, null));

So können Sie die Methode mit dem Wert auf dem Stapel nicht einfach anrufen kann, weil es ein Werttyp ist (obwohl könnte man, wenn es sich um eine Referenz-Typ war).

Was möchte ich zu erreichen (oder zu wissen, ob es möglich ist) ist es, die drei Zeilen zu ersetzen, die auf Kommentar angezeigt, aber das Programm weiter arbeiten, ohne eine temporäre lokale Verwendung.

War es hilfreich?

Lösung

Wenn die Variable auf dem Stapel ist bereits, können Sie voran gehen und emittieren nur den Methodenaufruf.

Es scheint, dass der Konstruktor die Variable nicht auf dem Stapel nicht drückt in einer typisierten Form. Nach dem Graben in die IL ein wenig, scheint es, gibt es zwei Möglichkeiten für die Verwendung der variablen, nachdem es zu konstruieren.

Sie können die Variable geladen werden, die den Verweis auf dem Auswertungsstapel gespeichert werden, bevor Sie den Konstruktor aufrufen und dann erneut diese Variable laden, nachdem der Konstruktor wie so Aufruf:

DynamicMethod method = new DynamicMethod("M", typeof(bool), Type.EmptyTypes);
ILGenerator il = method.GetILGenerator();
Type nullable = typeof(Nullable<int>);
ConstructorInfo ctor = nullable.GetConstructor(new Type[] { typeof(int) });
MethodInfo getValue = nullable.GetProperty("HasValue").GetGetMethod();
LocalBuilder value = il.DeclareLocal(nullable);         

// load the variable to assign the value from the ctor to
il.Emit(OpCodes.Ldloca_S, value);
// load constructor args
il.Emit(OpCodes.Ldc_I4_6);
il.Emit(OpCodes.Call, ctor);
il.Emit(OpCodes.Ldloca_S, value);

il.Emit(OpCodes.Call, getValue);
il.Emit(OpCodes.Ret);
Console.WriteLine(method.Invoke(null, null));

Die andere Option ist, es zu tun, wie Sie gezeigt haben. Der einzige Grund dafür, dass ich sehen kann, ist, dass die Ctor Methoden void zurückgeben, so dass sie ihren Wert nicht wie andere Methoden auf den Stapel gelegt. Es scheint seltsam, dass Sie Setloc nennen kann, wenn das neue Objekt nicht auf dem Stapel ist.

Andere Tipps

Nachdem die Optionen suchen, um etwas mehr und weitere Überlegung, ich glaube, du hast recht in der Annahme, es nicht getan werden kann. Wenn Sie den Stapel Verhalten von MSIL Anweisungen untersuchen, können Sie sehen, dass kein op seine Operanden verlässt (n) auf dem Stapel. Da dies eine Voraussetzung für ein ‚get-Adresse des Stack-Eintrag‘ wäre op, ich bin ziemlich sicher, nicht existiert.

Das lässt Sie entweder mit dup + Box oder stloc + ldloca. Sie haben darauf hingewiesen haben, letztere ist wahrscheinlich effizienter zu gestalten.

@ Greg: Viele Befehle lassen ihre Ergebnis auf dem Stapel, aber keine Anweisungen lassen alle ihre Operanden auf dem Stapel, die für ein ‚get-Stack erforderlich wäre, Elementadresse Anweisung.

ich es herausgefunden! Zum Glück war ich über die unbox Opcode zu lesen und festgestellt, dass es die Adresse drückt des Wertes. unbox.any drückt den Istwert. Also, um ein Verfahren auf einem Werttyp zu nennen, ohne sie in einer lokalen Variablen speichern zu müssen und dann laden ihre Adresse, können Sie einfach box von unbox gefolgt. Mit Ihrem letzten Beispiel:

var m = new DynamicMethod("M", typeof(bool), Type.EmptyTypes);
var il = m.GetILGenerator();
var ctor = typeof(Nullable<int>).GetConstructor(new[] { typeof(int) });
il.Emit(OpCodes.Ldc_I4_6);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Box, typeof(Nullable<int>)); // box followed by unbox
il.Emit(OpCodes.Unbox, typeof(Nullable<int>));
var getValue = typeof(Nullable<int>).GetMethod("get_HasValue");
il.Emit(OpCodes.Call, getValue);
il.Emit(OpCodes.Ret);
Console.WriteLine(m.Invoke(null, null));

Der Nachteil ist, dass Boxen Speicherzuweisung für das Box-Objekt verursacht, so ist es ein wenig langsamer als lokale Variablen (die bereits zugeordnet würden). Aber, es erspart Ihnen, zu bestimmen, zu erklären, und verweisen alle lokalen Variablen, die Sie benötigen.

schrieb nur eine Klasse, die tut, was der OP fragt ... hier ist der IL-Code, der C # Compiler erzeugt:

  IL_0008:  ldarg.0
  IL_0009:  ldarg.1
  IL_000a:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_000f:  stfld      valuetype [mscorlib]System.Nullable`1<int32> ConsoleApplication3.Temptress::_X
  IL_0014:  nop
  IL_0015:  ret
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top