Wickeln Sie ein IEnumerable und abfangen von Ausnahmen
Habe ich eine Reihe von Klassen, können Process()
Objekte, und kehren Sie Ihre eigenen Objekte:
public override IEnumerable<T> Process(IEnumerable<T> incoming) { ... }
Ich schreiben möchte eine Prozessor-Klasse, wickeln kann man diese Prozessoren, und melden Sie eine nicht abgefangene Ausnahmen, die die umschlossene Process()
Methode auslösen könnte. Meine erste Idee war etwas wie:
public override IEnumerable<T> Process(IEnumerable<T> incoming) {
try {
foreach (var x in this.processor.Process(incoming)) {
yield return x;
}
} catch (Exception e) {
WriteToLog(e);
throw;
}
}
aber das funktioniert nicht, wegen CS1626: Nicht Ertrag einen Wert im Körper von einem try-block mit einem catch-Klausel.
So, ich möchte etwas schreiben, dass das begrifflich gleichwertig, aber kompiliert. 🙂 Ich habe diese:
public override IEnumerable<T> Process(IEnumerable<T> incoming) {
IEnumerator<T> walker;
try {
walker = this.processor.Process(incoming).GetEnumerator();
} catch (Exception e) {
WriteToLog(e);
throw;
}
while (true) {
T value;
try {
if (!walker.MoveNext()) {
break;
}
value = walker.Current;
} catch (Exception e) {
WriteToLog(e);
throw;
}
yield return value;
}
}
aber das ist komplexer als ich gehofft hatte, und ich bin mir nicht ganz sicher, entweder die Richtigkeit oder, dass es nicht eine viel einfachere Möglichkeit.
Bin ich auf dem richtigen Weg? Gibt es einen einfacheren Weg?
- Was über die Implementierung von IEnumerable<T> direkt und abfangen von Ausnahmen in Ihrem MoveNext und Current?
- Ich denke, das ist die richtige Antwort. Sie sollten es so. Nichts falsch mit, kurz und bündig. 🙂
- Warum nicht einfach entfernen Sie die foreach-Schleife und Ertrag?
- möglich, Duplikat der LINQ-Abfrage durchführen, eine Projektion, überspringen Fällen, in denen der Projektion würde zu einer exception führen
- Das problem der "gleichwertigen" code ist, dass die
walker
variable ist nicht richtigDispose
ed. Sie sollten immerDispose
IEnumerator
s, da Sie die Ressourcen enthalten.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn das, was Sie wollen zu tun ist, behandeln einer Ausnahme während der Verarbeitung des Ergebnisses einer Aufzählung, dann versucht man die Logik einfach gehen muss, direkt in Ihrem for/while-Schleife.
Aber dein Beispiel liest sich, als ob Sie versuchen, zu fangen und zu überspringen, von Ausnahmen die durch die enumeration-Anbieter.
Soweit ich feststellen kann, gibt es keine Möglichkeit in C# Iteration über einen enumerator und überspringen und Ausnahme tritt innerhalb der enumerator selbst.
Wenn der enumerator eine Ausnahme auslöst, dann werden alle zukünftigen Aufrufe von MoveNext() führt zu falschen Ausgang.
Der einfachste Weg, um zu erklären, warum dies geschieht, ist mit diesem sehr einfach enumerable:
Verständlich, wenn wir bei diesem Beispiel ist es offensichtlich, dass die geworfene exception Ursache für diesen ganzen block zu verlassen, und die 3. und 4. yield-Anweisung wird nicht immer verarbeitet werden. In der Tat das übliche "Unreachable code erkannt' compiler-Warnung auf der 3. yield-Anweisung.
Also, wenn das Durchlaufen ist ein komplexer, gelten die gleichen Regeln:
Wenn die Ausnahme ausgelöst wird, wird die Verarbeitung dieser block aufhört und geht zurück bis den call-stack auf die nächste exception-handler.
ALLE brauchbare Beispiele zu bekommen, um dieses Problem, das ich gefunden habe ALSO nicht gehen, um das nächste Element in der Aufzählung, Sie alle Pause auf die erste Ausnahme.
Daher überspringen en Ausnahme in der Aufzählung müssen Sie den Anbieter, um es zu erleichtern. Dies ist nur möglich, wirklich, wenn Ihr codiert die Anbieter an oder Kontaktieren Sie die Entwickler haben, das folgende ist ein stark vereinfacht Beispiel, wie könnte man dies erreichen:
...
Dies ergibt die folgende Ausgabe:
Ich hoffe, das klärt das Problem für andere Entwickler in der Zukunft, wie es ist eine ziemlich Allgemeine Idee, dass wir alle, sobald wir anfangen, tief in Linq und Aufzählungen. Leistungsstarke Sachen, aber es gibt einige logische Einschränkungen.
Einer linq-extension geschrieben werden können, überspringen Sie alle Elemente, die zu einer Ausnahme führen, und übergeben es an eine logging-Funktion.
Beispiel:
Könnte es schöner sein, wenn die
process
Objekt war etwas, das verarbeitet nur ein Element in der Liste zu einem Zeitpunkt (wenn das überhaupt möglich ist), so dass Sie sammeln konnte jede einzelne Ausnahme und eineAggregateException
genau wie die Task Parallel library stellt.[Wenn Verfahren arbeitet auf der Liste als ganzes, oder wenn eine einzige Ausnahme muss zum Abbruch des gesamten Prozesses offensichtlich ist dieser Vorschlag ist nicht geeignet.]
Statt der Lösung Ihrer spezifischen problem nur, Sie können diese helper-Funktionen in Ihrem toolkit:
WithExceptionHandler<T>
wandelt einIEnumerable<T>
in ein anderes, und wenn Ausnahme Auftritt, wird dieonException
callback wird aufgerufen. Auf diese Weise können Sie Ihreprocess
- Funktion implementiert, wie dieses:In diesem way, Sie können auch etwas anderes tun, wie ignorieren der Ausnahme vollständig und lassen Sie die iteration wird einfach angehalten. Sie können auch passen Sie es ein wenig durch verwendet eine
Func<Exception, bool>
stattAction<Exception>
und erlaubt die exception-callback zu entscheiden, wird die iteration fortgesetzt werden oder nicht.IEnumerator
dass die Umgehung dieser Grenze. Obwohl verlassen Sie sich nicht auf einem anderen system, jedenfalls, die Sie verwenden können, für Ihren eigenen code.Schleife und Ertrag werden nicht benötigt (zumindest in deinem Beispiel). Einfach entfernen und es ist nicht mehr eine Beschränkung auf die catch-Blöcke.
Ich gehe davon aus, dass
this.processor.Process(incoming)
gibt eine collection zurück, die UmsetzungIEnumerable<T>
daher müssen Sie nicht erstellen Sie einen neuen iterator. Wennthis.processor.Process(incoming)
faul ist die Bewertung dannthis.processor.Process(incoming)
faul ist?GetEnumerator()
dann Ihren code wird nicht fangen, weil es nie ruftGetEnumerator()
.IEnumerable
ist faul-das ist, was es bedeutet, für einenGetEnumerator()
Methode zurückgibtIEnumerator
. Daher sind alle Verwendungen vonIEnumerable
sind automatisch faul. Ihr update für Ihre Antwort technisch arbeiten, aber es etwas Ruinen der pull-as-you-go-Vertrag von IEnumerable. Da Matt ' s Vorschlag würde nicht zeigen, dass die Einschränkung, ziehe ich seine Antwort.GetEnumerator()
etwas passiert ist (wie die Instanziierung der enumerator) - bevor dann sind Sie einfach vorbei Referenzen.