Verweis auf eine Ausnahme in finally-Block in einem Iterator

8

Gibt es eine Möglichkeit, einen Verweis auf eine Ausnahme innerhalb eines finally-Blocks in einem Iterator-Funktion oder -Eigenschaft, die try..finally, aber nicht try..catch erlauben ?

Ich werde es nicht benutzen, um den Kontrollfluss zu ändern oder zu verwirren, aber ich möchte trotzdem einen Verweis auf die Ausnahme im finally-Block erhalten (wenn einer geworfen wurde), um davon zu lesen es und möglicherweise fügen Sie Sachen zum Datenmitglied hinzu.

Ich verstehe, dass es aufgrund der Art der vom Compiler erzeugten Klassen von Iteratoren aus dem gleichen Grund wahrscheinlich nicht möglich / erlaubt ist, warum try..catch um eine yield-Anweisung überhaupt nicht erlaubt ist. Aber ich hoffe immer noch, dass es vielleicht einen Weg gibt (oder sogar einen hässlichen Trick), um die Ausnahme trotzdem zu bekommen.

Vereinfachtes Beispiel:

%Vor%     
KristoferA 17.01.2011, 07:26
quelle

4 Antworten

17

Das ist eine sehr interessante Frage.

Erinnern Sie sich daran, dass in Linq viele Standardoperatoren bereitgestellt werden, die effektiv miteinander ketten. Es gibt derzeit keinen, mit dem Sie benutzerdefinierte Ausnahmebehandlung um eine innere Sequenz wickeln können.

Ich schlage also vor, eine neue zu schreiben, mit der Sie eine Aktion angeben können, die jede Ausnahme behandelt, die während der Ausführung von IEnumerator.MoveNext auftritt:

%Vor%

Nun angenommen, wir hätten das:

%Vor%

Wir wären in der Lage, den ganzen Körper in eine try / catch zu verpacken, was leider illegal ist. Aber wir können die generierte Sequenz umschließen:

%Vor%

Das heißt, ich erhalte eine Sequenz, indem ich die "rohe" StringYielder -Methode aufruft, und dann wende ich den neuen Catch -Operator an und gebe an, was zu tun ist, wenn ein bestimmter Ausnahmetyp auftritt. Hier werde ich nur die Stack-Trace drucken.

Also wenn ich das tue:

%Vor%

Das Programm wird ohne Absturz beendet und die Ausgabe lautet:

%Vor%

Obwohl Sie den Code in der ursprünglichen Iterator-Methode nicht umschreiben können, können Sie ihn jetzt außerhalb der Iterator-Methode umschließen. Es ist wie eine nicht aufdringliche Art, die Ausnahmebehandlung um den Code zwischen jedem yield return einzufügen.

Bonusupdate!

Um wirklich wählerisch zu sein, wie ich diesen letzten Satz formuliert habe:

  • Erstens können Sie vor dem ersten yield return werfen und es wird auf die gleiche Weise behandelt, da dieser Code beim ersten Aufruf von MoveNext ausgeführt wird. Also "... der Code vor jeder ..." wäre genauer gewesen als "... der Code zwischen jeder ...".

  • Zweitens kann ein yield return einen Ausdruck akzeptieren, der ausgewertet werden muss und der während der Auswertung ausgegeben werden kann. Das sollte als Code angesehen werden, der vor dem yield return ausgeführt wird, obwohl er syntaktisch danach erscheint.

Daniel Earwicker 19.01.2011, 14:16
quelle
5

Wie wäre es, den gesamten Code, der eine Ausnahme erzeugen könnte, in einen verschachtelten try / catch zu verschieben:

%Vor%

Mit dem obigen Muster können Sie jedoch alles tun, was Sie brauchen, nur innerhalb des catch-Blocks, was einfacher wäre - Sie könnten den umgebenden try / finally-Block loswerden.

    
Jon Skeet 17.01.2011 07:32
quelle
2

Zu wissen, welche Ausnahme während eines Finalizers noch aussteht, ist eine nette Fähigkeit. VB.net macht es möglich, obwohl peinlich. C # nicht. In vb.net ist die Technik:

%Vor%

Beachten Sie, dass dies eine sehr nützliche Technik sein kann, wenn Code, der durch eine Exception ausgelöst wird, garantiert dazu führt, dass entweder die Exception erneut ausgelöst oder eine neue Aggregatausnahme ausgelöst wird. Wenn die Ausnahme schließlich nicht behandelt wird, wird der Debugger-Trap "Unhandled Exception" ausgelöst, wenn die ursprüngliche Ausnahme auftritt und nicht beim letzten Mal. Dies kann das Debuggen erheblich erleichtern, da dem Debugger eine Menge an Programmstatus zur Verfügung steht, der andernfalls zerstört werden würde.

Beachten Sie auch, dass die PendingException am Ende des Try-Hauptblocks explizit gelöscht wird. Es ist möglich, dass PendingException einen Wert hat, auch wenn keine ausstehende Ausnahme vorliegt. Dies kann auftreten, wenn etwas in einem doppelt verschachtelten Try-Block eine Ausnahme auslöst, die nicht in unserem try-Block gefangen wird, aber die Finally-Klausel des internen Try-Blocks eine Ausnahme auslöst, die innerhalb des einfach verschachtelten Try-Blocks gefangen wird. In diesem Fall verschwindet die ursprüngliche Ausnahme effektiv. Es kann sinnvoll sein, einen speziellen Protokolleintrag zu generieren, wenn entweder "CopyFirstParameterToSecondAndReturnFalse" oder "PendingException = Nothing" ausgeführt wird, wenn PendingException ungleich null ist, da dieses Szenario wahrscheinlich einen Fehler darstellt, der nirgendwo sonst protokolliert werden kann. Wenn es jedoch mehrere verschachtelte catch-Blöcke gibt, könnten sie redundante Protokolleinträge erzeugen.

Da C # die für diesen Ansatz erforderliche Ausnahmefilterung nicht unterstützt, kann es hilfreich sein, einen VB-Wrapper zu schreiben, der C # -Delegaten aufrufen kann, aber die erforderliche Ausnahmebehandlungslogik bereitstellt.

BEARBEITEN Es ist nicht möglich, eine Rendite in einem try-catch-Block zu erzielen, aber ich würde nicht sehen, dass dies ein Problem mit etwas wie:

verursachen würde %Vor%     
supercat 17.01.2011 16:54
quelle
1

finally Blöcke sind für immer ausgeführtes Aufräumen gedacht und sind daher nicht dazu bestimmt, mit Ausnahmen zu manipulieren - es kann leicht keine ausstehende Ausnahme geben.

Die Verwendung des Blocks catch mit einem erneuten Durchlauf sollte für Sie funktionieren:

%Vor%     
Ondrej Tucny 17.01.2011 07:30
quelle

Tags und Links