2016-05-11 21 views
1

ich eine dynamische Methode, d.h .:C# Emit IL - Push-Wert eines Objekts auf Stapel?

class MyClass 
{ 
    private void object _o = <whatever>; 

    void CreateDynamicMethod() 
    { 
     DynamicMethod dm = new DynamicMethod("Test", ...); 

     // emit various IL 

     // need to push _o onto stack here 
    } 
} 

Der zweite Kommentar, ich möchte den Wert von _o derzeit auf den Stapel schieben. Ich möchte nicht die Referenz von _o drücken, da sie sich ändern wird, wenn ich die dynamische Methode erstelle.

Also sagen wir _o = 5, möchte ich die 5 drücken, wenn _o eine Liste enthält, möchte ich das schieben, wenn es eine Zeichenfolge enthält, möchte ich die Zeichenfolge drücken.

Die dynamische Methode (Test) ist eine statische Methode und es wird offensichtlich nicht den This-Zeiger von MyClass haben, um das Feld zu erhalten. Ich kann es nicht einmal in eine statische Variable einfügen, auf die während der Ausführung von Test() zugegriffen wird, und nicht während ich die Methode erstelle.

Auch wenn ich den This-Zeiger hatte, wird _o nicht den richtigen Wert bei Test() Ausführungszeit haben.

Irgendwelche Vorschläge?

Antwort

1

Sie können einen Zeiger nicht übergeben, weil er ungültig ist. Sie können jedoch ein Handle übergeben. Eine Technik, die ich in der Vergangenheit verwendet habe, besteht darin, einen ganzzahligen Wert als Index in eine statisch verfügbare Ressource (wie ein Wörterbuch in einer statischen Klasse oder einer Netzwerkressource oder was auch immer) auf den Stapel zu schieben, der den Wert des Objekts enthält Zukünftig wird die Methode es brauchen.

So führt der Compiler übrigens virtuelle Methodenaufrufe durch. Es weiß nicht, welche Version einer Instanzmethode zur Kompilierzeit aufgerufen wird - es hat keine Ahnung. Daher wird ein Token, das die Methode darstellt, auf den Stapel geschoben, anstatt einen Zeiger auf die Methode selbst. Dieses Token wird dann verwendet, um zur Laufzeit eine Vtable-Suche durchzuführen, wobei eine Ressource an einer bekannten Position mit dem Token als Index für diese Ressource verwendet wird.

Alles, was Sie zur Ausführung Ihrer Aufgabe benötigen, ist Ihre eigene Version einer vtable, und Sie verwenden eine Ganzzahl als Token. Ldc.i4.s ist die IL-Anweisung, um eine beliebige ganzzahlige Konstante auf den Stapel zu schieben. Das ist Ihr Token :) Schieben Sie es auf den Stapel, bevor Sie eine statische Methode zum Abrufen Ihres Objekts aufrufen.

Achten Sie darauf, das Objekt zu speichern, bevor die Methode obwohl genannt wird;)

Es gibt Möglichkeiten, um den Stapel zu manipulieren, um ein serialisierte Proxy-Objekt zu speichern, dass das Verfahren könnte dann deserialisieren, wenn sie aufgerufen wird. Aber ich werde entflammt, wenn ich das hier erkläre.

+1

Sie meinen eine GCHandle? In meinem Builder-Code habe ich: Objekt o = blah; GCHandle.Alloc (o). ToIntPtr(). ToInt64() mit einem Opcodes.Ldc_I8 und es stürzte nicht, aber als ich versuchte, den Wert im Debugger anzuzeigen, konnte es auf Speicherfehler nicht zugreifen. – SledgeHammer

+0

@SledgeHammer - Nein, ich meine nicht ein GC-Handle. Ich meine, speichern Sie das Objekt und legen Sie es irgendwo weg, in einem Array, einer Datei, was auch immer. Haben Sie einfach eine nette statische Methode irgendwo, die einen int-Parameter übernimmt und das Objekt an Sie zurückgibt, und dann können Sie diese ganze Zahl auf den Stapel schieben, wobei Sie wissen, dass Ihre statische Methode es als Schlüssel zum späteren Abrufen des Objekts verwenden wird. Um es anders auszudrücken - das einzige Objekt, das Sie zum Kompilieren auf den Stapel schieben können, ist int oder ein Token für ein String-Literal (Sie können ein großes Objekt in einem davon halten!) – hoodaticus

+1

Ja, ich hatte diese Idee früher eine statische Methode/Wörterbuch, aber es schien hacky. LOL. Dachte, es könnte einen besseren Weg geben. Raten Sie nicht :(. Danke. – SledgeHammer