(Zunächst einmal ist dies ein sehr langer Post, aber keine Sorge: Ich habe bereits alles implementiert, ich frage nur nach Ihrer Meinung oder nach möglichen Alternativen.)
Ich habe Probleme bei der Implementierung des Folgenden; Ich würde etwas Hilfe schätzen:
Type
als Parameter. Ich erstelle eine Eigenschaft pro Feld der ursprünglichen Klasse, wie folgt:
%Vor% Für jede Methode der Superklasse erstelle ich eine analoge Methode in der Unterklasse. Der Body der Methode muss derselbe sein, außer dass ich die Anweisungen ldfld x
durch callvirt this.get_X
ersetze, das heißt, anstatt das Feld direkt zu lesen, rufe ich den get-Accessor auf.
Ich habe Probleme mit Schritt 4. Ich weiß, dass Sie Code nicht so manipulieren sollen, aber ich muss wirklich.
Folgendes habe ich versucht:
Versuch Nr. 1: Verwenden Sie Mono.Cecil. Dies würde es mir erlauben, den Körper der Methode in menschenlesbare Instructions
zu analysieren und Anweisungen leicht zu ersetzen. Der ursprüngliche Typ befindet sich jedoch nicht in einer DLL-Datei, daher kann ich keine Möglichkeit finden, ihn mit Mono.Cecil zu laden. Schreiben Sie den Typ in eine DLL, dann laden Sie es, dann ändern Sie es und schreiben Sie den neuen Typ auf die Festplatte (was ich denke, ist die Art, wie Sie einen Typ mit Mono.Cecil erstellen), und dann lädt es scheint wie ein riesig Overhead.
Versuch 2: Verwenden Sie Mono.Reflection. Dies würde mir auch ermöglichen, den Körper in Instructions
zu analysieren, aber dann habe ich keine Unterstützung für das Ersetzen von Anweisungen. Ich habe eine sehr hässliche und ineffiziente Lösung mit Mono.Reflection implementiert, aber es unterstützt noch keine Methoden, die try-catch-Anweisungen enthalten (obwohl ich denke, dass ich das implementieren kann) und ich bin besorgt, dass es andere Szenarien geben könnte was es nicht funktioniert, da ich den ILGenerator
auf eine etwas ungewöhnliche Weise benutze. Außerdem ist es sehr hässlich;). Folgendes habe ich getan:
Und hier kommt die schreckliche, schreckliche Methode Reemit
:
Verzweigungsanweisungen sind ein Sonderfall, weil instr.Operand
ist SByte
, aber Emit
erwartet einen Operanden vom Typ Label
. Daher die Notwendigkeit für die Dictionary labels
.
Wie Sie sehen können, ist das ziemlich schrecklich. Außerdem funktioniert es nicht in allen Fällen, beispielsweise bei Methoden, die try-catch-Anweisungen enthalten, da ich sie nicht mit den Methoden BeginExceptionBlock
, BeginCatchBlock
usw. von ILGenerator
ausgegeben habe. Das wird kompliziert. Ich denke, ich kann es tun: MethodBody
hat eine Liste von ExceptionHandlingClause
, die die notwendigen Informationen enthalten sollte, um dies zu tun. Aber ich mag diese Lösung sowieso nicht, also werde ich das als letzte Lösung speichern.
Versuch # 3: Gehen Sie bare-back und kopieren Sie einfach das by MethodBody.GetILAsByteArray()
zurückgegebene Byte-Array, da ich nur einen einzelnen Befehl für einen anderen einzelnen Befehl der gleichen Größe ersetzen möchte, der den genau dasselbe Ergebnis: es lädt den gleichen Objekttyp auf den Stapel usw. Es werden also keine Labels verschoben und alles sollte genauso funktionieren. Ich habe dies getan, bestimmte Bytes des Arrays ersetzt und dann MethodBuilder.CreateMethodBody(byte[], int)
aufgerufen, aber ich bekomme immer noch den gleichen Fehler mit Ausnahmen, und ich muss immer noch die lokalen Variablen deklarieren oder ich werde einen Fehler bekommen ... auch wenn Ich kopiere einfach den Körper der Methode und ändere nichts.
Das ist also effizienter, aber ich muss mich immer noch um die Ausnahmen kümmern, usw.
Seufz.
Hier ist die Implementierung von Versuch # 3, falls jemand interessiert ist:
%Vor%(Ich weiß, dass es nicht schön ist. Tut mir leid. Ich habe es schnell zusammengefügt, um zu sehen, ob es funktionieren würde.)
Ich habe nicht viel Hoffnung, aber kann irgendjemand etwas Besseres vorschlagen?
Tut mir leid wegen der extrem langen Post und danke.
UPDATE # 1: Aghh ... Ich habe gerade diese in der msdn-Dokumentation :
[Die CreateMethodBody-Methode] ist derzeit nicht vollständig unterstützt. Das Der Benutzer kann den Standort nicht angeben Token-Fix-Ups und Exception-Handler.
Ich sollte wirklich die Dokumentation lesen, bevor ich etwas versuche. Eines Tages werde ich lernen ...
Das bedeutet, dass Option # 3 try-catch-Anweisungen nicht unterstützen kann, was es für mich nutzlos macht. Muss ich wirklich die schreckliche # 2 benutzen? :/ Hilfe! : P
UPDATE # 2: Ich habe erfolgreich # 2 mit Unterstützung für Ausnahmen implementiert. Es ist ziemlich hässlich, aber es funktioniert. Ich poste es hier, wenn ich den Code ein wenig verfeinere. Es ist keine Priorität, also kann es in ein paar Wochen sein. Lass dich einfach wissen, falls sich jemand dafür interessiert.
Danke für Ihre Vorschläge.
Ich versuche, eine sehr ähnliche Sache zu machen. Ich habe bereits Ihren # 1 Ansatz ausprobiert, und ich stimme zu, dass dies einen enormen Overhead verursacht (ich habe es jedoch nicht genau gemessen).
Es gibt eine DynamicMethod Klasse, die laut MSDN ist - "Definiert und stellt eine dynamische Methode dar, die kompiliert, ausgeführt und verworfen werden kann. Aussortierte Methoden sind für die Garbage Collection verfügbar."
Leistung klingt es gut.
Mit der ILReader Bibliothek I könnte MethodInfo in DynamicMethod . Wenn Sie in die ConvertFrom-Methode der DyanmicMethodHelper-Klasse der finden Sie den Code, den wir benötigen:
%Vor%Lassen Sie uns theoretisch den Code einer bestehenden Methode modifizieren und als dynamische Methode ausführen.
Mein einziges Problem ist jetzt, dass Mono.Cecil uns nicht erlaubt, den Bytecode einer Methode zu speichern (zumindest konnte ich den Weg dazu nicht finden). Wenn Sie den Mono.Cecil-Quellcode herunterladen, verfügt er über eine CodeWriter-Klasse, um die Aufgabe auszuführen, ist jedoch nicht öffentlich.
Ein anderes Problem, das ich bei diesem Ansatz habe, ist das MethodInfo - & gt; DynamicMethod-Umwandlung funktioniert nur mit statischen Methoden mit ILReader . Aber das kann man umgehen.
Die Ausführung des Aufrufs hängt von der Methode ab, die ich verwendet habe. Ich habe folgende Ergebnisse erhalten, nachdem ich 10'000'000 mal kurz die Methode aufgerufen habe:
Als nächstes werde ich versuchen:
Es hört sich nach viel Arbeit an und es funktioniert vielleicht nicht, wir werden sehen:)
Ich hoffe, es hilft, lassen Sie mich wissen, was Sie denken.
Haben Sie PostSharp ausprobiert? Ich denke, dass es über die Ein Feldzugriffsaspekt .
Vielleicht habe ich etwas falsch verstanden, aber wenn Sie eine bestehende Instanz einer Klasse erweitern möchten, können Sie in Burg Dynamischer Proxy .
Grundsätzlich kopieren Sie den Programmtext der ursprünglichen Klasse und nehmen dann regelmäßig Änderungen daran vor. Ihre aktuelle Methode besteht darin, den Objekt Code für die Klasse zu kopieren und diesen zu patchen. Ich kann verstehen, warum das hässlich aussieht; Du arbeitest auf einem extrem niedrigen Level.
Dies scheint so zu sein, als wäre es leicht, mit Quell-zu-Quell-Programmtransformationen zu arbeiten. Dies funktioniert auf der AST für den Quellcode und nicht der Quellcode selbst für die Genauigkeit. Siehe DMS Software Reengineering Toolkit für ein solches Tool. DMS hat einen vollständigen C # 4.0-Parser.
Was ist mit der Verwendung von SetMethodBody anstelle von CreateMethodBody (das wäre eine Variation von # 3)? Es ist eine neue Methode in .NET 4.5 eingeführt und scheint Ausnahmen und Korrekturen zu unterstützen.
Tags und Links c# reflection reflection.emit cil mono.cecil