Folgendes ist in Ordnung:
try
{
Console.WriteLine("Before");
yield return 1;
Console.WriteLine("After");
}
finally
{
Console.WriteLine("Done");
}
Der finally
Block wird ausgeführt, wenn die Ausführung des gesamten Objekts abgeschlossen ist ( IEnumerator<T>
unterstützt IDisposable
eine Möglichkeit, dies sicherzustellen, auch wenn die Aufzählung vor Abschluss abgebrochen wird).
Das ist aber nicht in Ordnung:
try
{
Console.WriteLine("Before");
yield return 1; // error CS1626: Cannot yield a value in the body of a try block with a catch clause
Console.WriteLine("After");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Angenommen, (aus Gründen der Argumentation) wird eine Ausnahme von dem einen oder anderen WriteLine
Aufruf im try-Block ausgelöst . Was ist das Problem bei der Fortsetzung der Ausführung im catch
Block?
Natürlich kann der Ertragsrendite-Teil (derzeit) nichts werfen, aber warum sollte uns das davon abhalten, ein Einschließen try
/ catch
Ausnahmen zu behandeln, die vor oder nach einem geworfen werden yield return
?
Update: Es gibt hier einen interessanten Kommentar von Eric Lippert - anscheinend haben sie bereits genug Probleme, das try / finally-Verhalten korrekt zu implementieren!
BEARBEITEN: Die MSDN-Seite zu diesem Fehler lautet: http://msdn.microsoft.com/en-us/library/cs1x15az.aspx . Es erklärt jedoch nicht warum.
quelle
Antworten:
Ich vermute, dass dies eher eine Frage der Praktikabilität als der Machbarkeit ist. Ich vermute, dass es sehr, sehr wenige Male gibt, in denen diese Einschränkung tatsächlich ein Problem ist, das nicht umgangen werden kann - aber die zusätzliche Komplexität im Compiler wäre sehr bedeutend.
Es gibt einige Dinge wie diese, die mir bereits begegnet sind:
In jedem dieser Fälle wäre es möglich, auf Kosten der zusätzlichen Komplexität des Compilers ein wenig mehr Freiheit zu gewinnen. Das Team traf die pragmatische Wahl, für die ich sie begrüße - ich hätte lieber eine etwas restriktivere Sprache mit einem zu 99,9% genauen Compiler (ja, es gibt Fehler; ich bin neulich auf SO gestoßen) als eine mehr flexible Sprache, die nicht richtig kompiliert werden konnte.
EDIT: Hier ist ein Pseudo-Beweis dafür, warum es machbar ist.
Berücksichtige das:
Nun transformiere:
in (Art von Pseudocode):
Die einzige Überschneidung besteht darin, Try / Catch-Blöcke einzurichten - aber das kann der Compiler auf jeden Fall.
Vielleicht habe ich hier etwas verpasst - wenn ja, lassen Sie es mich bitte wissen!
quelle
using
und zu erstellenforeach
. Zum Beispiel:try{foreach (string s in c){yield return s;}}catch(Exception){}
yield
meiner Meinung nach aufgrund des Spaghetti-Codes, den Sie schreiben müssen, um ihn zu umgehen, immer noch stark ein .yield
- es ist weit davon entfernt, ernsthaft zu sein .Alle
yield
Anweisungen in einer Iteratordefinition werden in einen Zustand in einer Zustandsmaschine konvertiert, die eineswitch
Anweisung effektiv verwendet , um Zustände voranzutreiben. Wenn es tut generieren Code füryield
Anweisungen in einem try / catch müßte es kopieren alles in demtry
für den Block jederyield
Aussage , während jeden zweiter ohneyield
Erklärung für diesen Block. Dies ist nicht immer möglich, insbesondere wenn eineyield
Anweisung von einer früheren abhängig ist.quelle
Ich würde spekulieren, dass es aufgrund der Art und Weise, wie der Aufrufstapel verwundet / abgewickelt wird, wenn Sie die Rendite eines Enumerators erzielen, für einen try / catch-Block unmöglich wird, die Ausnahme tatsächlich zu "fangen". (weil sich der Yield Return Block nicht auf dem Stack befindet, obwohl er den Iterationsblock erstellt hat)
Um eine Vorstellung davon zu bekommen, wovon ich spreche, richten Sie einen Iteratorblock und einen Foreach mit diesem Iterator ein. Überprüfen Sie, wie der Call Stack im foreach-Block aussieht, und überprüfen Sie ihn dann im iterator try / finally-Block.
quelle
Ich habe die Antwort von THE INVINCIBLE SKEET akzeptiert, bis jemand von Microsoft kommt, um kaltes Wasser auf die Idee zu gießen. Aber ich bin mit dem Teil der Meinungssache nicht einverstanden - natürlich ist ein korrekter Compiler wichtiger als ein vollständiger, aber der C # -Compiler ist bereits sehr geschickt darin, diese Transformation für uns zu klären, soweit dies der Fall ist. Ein wenig mehr Vollständigkeit in diesem Fall würde es einfacher machen, die Sprache zu verwenden, zu unterrichten, zu erklären, mit weniger Randfällen oder Fallstricken. Ich denke, es wäre die zusätzliche Mühe wert. Ein paar Leute in Redmond kratzen sich vierzehn Tage lang am Kopf, und infolgedessen können sich Millionen von Programmierern im nächsten Jahrzehnt etwas mehr entspannen.
(Ich habe auch den schmutzigen Wunsch, dass es eine Möglichkeit gibt
yield return
, eine Ausnahme zu machen , die "von außen" durch den Code, der die Iteration steuert, in die Zustandsmaschine gestopft wurde. Aber meine Gründe, dies zu wollen, sind ziemlich dunkel.)Eigentlich hat eine Frage, die ich zu Jons Antwort habe, mit dem Auslösen des Yield Return-Ausdrucks zu tun.
Offensichtlich ist die Rendite 10 nicht so schlecht. Aber das wäre schlecht:
Wäre es also nicht sinnvoller, dies im vorherigen Try / Catch-Block zu bewerten:
Das nächste Problem wären verschachtelte Try / Catch-Blöcke und erneut geworfene Ausnahmen:
Aber ich bin sicher, es ist möglich ...
quelle