2009-05-14 5 views
1

Wenn Sie eine Iteratorfunktion in C# schreiben, verwendet die Compiler-Syntax mithilfe der Yield-Syntax Ihre Funktion intern in eine Klasse. Also, wenn ich so etwas schreiben:Suchen des Objektfelds, das einem Referenzparameter in C# entspricht

IEnumerator<int> MyIterator() { 
    for (int i = 0; i < 10; i++) 
    yield return i; 
} 

Hervorrufen MyIterator() erstellt eine Klasse mit einem eigenen Bereich für i, anstatt einen Stapel Variable zu verwenden.

Jetzt für die Frage: Ich möchte einen Weg finden, das Feld zu finden, das verwendet wird, um den Wert von i zu speichern. Der Grund ist ein wenig beteiligt: ​​

Ich verwende Iterator-Funktionen für kooperative Threading. Ich schreibe einfachen Code wie diesen in einen Iterator, um die Ausführung einer Funktion zu unterbrechen, bis ein asynchroner Vorgang abgeschlossen ist.

Future<string> f; 
yield return file.readLine(out f); 
string line = f.Result; 

Wenn diese drei Anweisungen ausgeführt werden, wird die Readline Funktion aufgerufen und speichert eine Zukunft in die ‚f‘ Feld. Die Iterator-Funktion wird dann ausgesetzt und wacht wieder auf, wenn im 'f' Future ein Ergebnis gespeichert wurde, an dem ich das Ergebnis abrufen kann.

Was ich will, tun können, ist dies:

string line; 
yield return file.readLine(out line); 

Unter normalen Umständen ist dies nicht möglich sein würde, in .NET, da es keine Möglichkeit, dass ein Stapel Variable am Leben zu garantieren bleibt lang genug für mich, um daran schreiben zu können.

Allerdings, ich weiß, dass Iterator-Funktionen alle ihre Locals innerhalb von Feldern speichern, kann ich theoretisch dies tun, indem Sie einen Verweis auf den Iterator (am Leben zu halten) und auf das Feld selbst, so dass, wenn der readLine-Vorgang abgeschlossen ist kann das Ergebnis direkt in das Feld speichern.

Das Problem ist, dass die CLR scheint mir keinen Weg zu geben, dies zu tun.

Wenn ich einen Parameter 'out' oder 'ref' habe, kann ich die unsichere C# oder rohe MSIL verwenden, um diesen Parameter in einen rohen Zeiger, eine Referenz oder eine TypedReference zu konvertieren. Leider ist es nicht legal, einen dieser drei Typen in einem Feld zu speichern - selbst wenn Sie den Compiler dazu bringen, es Ihnen zu überlassen, lässt der Bytecode-Verifizierer den Code nicht ausführen. Dies liegt höchstwahrscheinlich daran, dass das Speichern eines Verweises auf eine Stapelvariable von Natur aus unsicher ist.

Also was ich suche, ist eine Funktion zu schreiben, die einen out/ref-Parameter verwendet, und die Felder des aufrufenden Iterators durchsucht, um ein entsprechendes Feld zu finden. Sobald das Feld vorhanden ist, speichert es einen Verweis auf den Iterator und das Feld in einen Wrappertyp und ordnet dem Wrapper eine asynchrone Operation zu.

Angesichts der Overhead in Laufzeit Reflektion in .NET, vermute ich, dass dies nicht möglich ist, ohne entweder meine Quellcode vorverarbeiten oder meine Assemblys nach der Kompilierung nachbearbeiten.

Allerdings würde ich gerne einige Vorschläge für alternative Lösungen hören. :) Irgendwelche Ideen?

Antwort

0

ich endlich eine Lösung gefunden, die meine Bedürfnisse hier erfüllt, und was wissen Sie - es LINQ beinhaltet.

Ein Blogbeitrag auf .NET erwähnt offhand, wie Sie jeden Ausdruck Lambda in einen Ausdruck <> Objekt konvertieren können, wenn Sie den Ergebnistyp des Ausdrucks kennen. Da stellt sich heraus, dass Sie einen Ausdruck wie dies erfolgen können:

() => this.Foo.Bar

und zu Fuß durch den Knoten mit einem Member (für ‚Bar‘), um am Ende und ein 'dieses' Objekt (für 'this.Foo'). Mit diesen beiden Werten können Sie direkt an ein Feld oder eine Eigenschaft binden, die/das bei der Kompilierung angegeben wurde. Im Fall einer Eigenschaft können Sie dies sehr effizient tun, indem Sie die Methoden Get und Set der Eigenschaft von MemberInfo abrufen und in stark typisierte Delegaten konvertieren, wodurch die Kosten für das Lesen/Schreiben dieser Eigenschaft sehr niedrig werden.

Das Beste von allem, das funktioniert perfekt mit automatisierten Refactoring und Quellcode-Suchen, weil es den Muskel des Compilers für Sie verwendet, und es keine Änderungen an vorhandenem Code erfordert, um Bindung zu ermöglichen.

Der einzige Nachteil ist, dass es sehr schwierig ist, an Felder/Eigenschaften einer Struktur zu binden, aber ich halte das sowieso nicht für einen besonders relevanten Anwendungsfall.

Wer dieses oder ein ähnliches Problem hier den Quellcode anzeigen können, lösen suchen:

http://code.google.com/p/fracture/source/browse/trunk/Squared/Util/Bind.cs

1

Monkeying mit den Feldern des Iterators verlangt nach Ärger. Wäre es nicht einfacher, ein Mitglied einer Klasse zu verwenden, die Sie in der Iteratorsignatur angeben? Entweder indem man es ein- oder ausgibt? Im Grunde eine Art einfache Wrapper-Klasse auf dem Weg?

class MyWrapper { 
    public string Line {get;set;} 
} 

so dass Sie Line ganz sicher, einfach über einen MyWrapper Objektverweis sprechen?

Ich habe nicht alle die Frage verstehen, so dass das Beste ist, was ich sagen kann ...

+0

Zukunft im Beispiel mehr oder weniger dient dem Zweck, die Sie beschreiben. Den Wert eines Containers jedes Mal aus der Box zu nehmen, ist ein Problem, also würde ich den Schritt überspringen und direkt in das Feld schreiben, wenn ich kann. Die Semantik des Änderns der Felder des Iterators scheint mir klar zu sein - wenn Sie die generierte IL für einen Iterator betrachten, ist es vollkommen sicher, die Locals zu modifizieren, solange Sie es vernünftig tun. –