Ich schaue auf den Artikel C # - Datenübertragungsobjekt über serialisierbare DTOs.
Der Artikel enthält diesen Code:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
throw ex;
}
}
Der Rest des Artikels sieht vernünftig und vernünftig aus (für einen Noob), aber dieser Try-Catch-Wurf löst eine WtfException aus ... Ist das nicht genau gleichbedeutend damit, Ausnahmen überhaupt nicht zu behandeln?
Ergo:
public static string SerializeDTO(DTO dto) {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
Oder fehlt mir etwas Grundlegendes zur Fehlerbehandlung in C #? Es ist so ziemlich das gleiche wie Java (minus geprüfte Ausnahmen), nicht wahr? ... Das heißt, beide haben C ++ verfeinert.
Die Frage zum Stapelüberlauf Der Unterschied zwischen dem erneuten Werfen von parameterlosem Fang und dem Nichtstun? scheint meine Behauptung zu unterstützen, dass Try-Catch-Throw ein No-Op ist.
BEARBEITEN:
Nur um es für alle zusammenzufassen, die diesen Thread in Zukunft finden ...
UNTERLASSEN SIE
try {
// Do stuff that might throw an exception
}
catch (Exception e) {
throw e; // This destroys the strack trace information!
}
Die Stack-Trace-Informationen können entscheidend sein, um die Grundursache des Problems zu ermitteln!
TUN
try {
// Do stuff that might throw an exception
}
catch (SqlException e) {
// Log it
if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
// Do special cleanup, like maybe closing the "dirty" database connection.
throw; // This preserves the stack trace
}
}
catch (IOException e) {
// Log it
throw;
}
catch (Exception e) {
// Log it
throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
// Normal clean goes here (like closing open files).
}
Fangen Sie die spezifischeren Ausnahmen vor den weniger spezifischen (genau wie Java).
Verweise:
quelle
Antworten:
Zuerst; Die Art und Weise, wie der Code im Artikel es tut, ist böse.
throw ex
setzt den Aufrufstapel in der Ausnahme auf den Punkt zurück, an dem sich diese throw-Anweisung befindet; Verlust der Informationen darüber, wo die Ausnahme tatsächlich erstellt wurde.Zweitens, wenn Sie nur so fangen und erneut werfen, sehe ich keinen Mehrwert. Das obige Codebeispiel wäre
throw ex
ohne den Try-Catch genauso gut (oder, angesichts des Bits, sogar noch besser).Es gibt jedoch Fälle, in denen Sie möglicherweise eine Ausnahme abfangen und erneut auslösen möchten. Die Protokollierung könnte eine davon sein:
quelle
ex
Objekt nicht verwenden möchten, müssen Sie es nicht instanziieren.throw ex
Startet in Java den Stacktrace nicht neu.Tu das nicht,
Sie verlieren die Stack-Trace-Informationen ...
Entweder tun,
ODER
Einer der Gründe, warum Sie möglicherweise erneut werfen möchten, ist, wenn Sie verschiedene Ausnahmen behandeln, z
quelle
C # (vor C # 6) unterstützt keine "gefilterten" Ausnahmen von CIL, was VB tut. In C # 1-5 besteht ein Grund für das erneute Auslösen einer Ausnahme darin, dass Sie zum Zeitpunkt von catch () nicht über genügend Informationen verfügen. um festzustellen, ob Sie die Ausnahme tatsächlich abfangen wollten.
In VB können Sie dies beispielsweise tun
... die MyExceptions mit unterschiedlichen ErrorCode-Werten nicht behandeln würden. In C # vor Version 6 müssten Sie die MyException abfangen und erneut auslösen, wenn der ErrorCode nicht 123 wäre:
Seit C # 6.0 können Sie genau wie mit VB filtern :
quelle
Mein Hauptgrund für Code wie:
ist so, dass ich einen Haltepunkt im catch haben kann, der ein instanziiertes Ausnahmeobjekt hat. Ich mache das oft beim Entwickeln / Debuggen. Natürlich gibt mir der Compiler eine Warnung zu allen nicht verwendeten E's, und im Idealfall sollten sie vor einem Release-Build entfernt werden.
Sie sind aber nett beim Debuggen.
quelle
#if DEBUG
um BEIDE herumtry {
und} catch () {...}
ist ein bisschen chaotisch und macht mich ehrlich gesagt mulmig ... Der Vorprozessor ist im Allgemeinen nicht mein Freund.Ein gültiger Grund für das erneute Auslösen von Ausnahmen kann sein, dass Sie der Ausnahme Informationen hinzufügen oder die ursprüngliche Ausnahme in eine eigene Ausnahme einschließen möchten:
quelle
Nicht genau, es ist nicht dasselbe. Es setzt den Stacktrace der Ausnahme zurück. Obwohl ich damit einverstanden bin, dass dies wahrscheinlich ein Fehler und damit ein Beispiel für schlechten Code ist.
quelle
Sie möchten nicht ex werfen, da dies den Aufrufstapel verliert. Siehe Ausnahmebehandlung (MSDN).
Und ja, der Versuch ... catch macht nichts Nützliches (abgesehen davon, dass der Aufrufstapel verloren geht - es ist also tatsächlich schlimmer - es sei denn, Sie wollten diese Informationen aus irgendeinem Grund nicht offenlegen).
quelle
Ein Punkt, den die Leute nicht erwähnt haben, ist, dass .NET-Sprachen zwar nicht wirklich unterscheiden, die Frage , ob man im Falle einer Ausnahme Maßnahmen ergreifen sollte und ob man sie lösen wird, jedoch unterschiedliche Fragen sind. Es gibt viele Fälle, in denen man aufgrund von Ausnahmen Maßnahmen ergreifen sollte, auf deren Lösung man keine Hoffnung hat, und es gibt einige Fälle, in denen alles, was zum "Auflösen" einer Ausnahme erforderlich ist, darin besteht, den Stapel bis zu einem bestimmten Punkt abzuwickeln - es sind keine weiteren Maßnahmen erforderlich .
Aufgrund der allgemeinen Weisheit, dass man nur Dinge "fangen" sollte, die man "handhaben" kann, tut dies eine Menge Code, der Maßnahmen ergreifen sollte, wenn Ausnahmen auftreten. Beispielsweise erhält eine Menge Code eine Sperre, versetzt das geschützte Objekt "vorübergehend" in einen Zustand, der seine Invarianten verletzt, versetzt es dann in einen legitimen Zustand und gibt die Sperre wieder frei, bevor andere das Objekt sehen können. Wenn eine Ausnahme auftritt, während sich das Objekt in einem gefährlich ungültigen Zustand befindet, besteht die übliche Praxis darin, die Sperre aufzuheben, während sich das Objekt noch in diesem Zustand befindet. Ein viel besseres Muster wäre, eine Ausnahme zu haben, die auftritt, während sich das Objekt in einem "gefährlichen" Zustand befindet, und die Sperre ausdrücklich ungültig macht, sodass jeder zukünftige Versuch, sie zu erwerben, sofort fehlschlägt.
In den meisten .NET-Sprachen besteht die einzige Möglichkeit für Code, basierend auf einer Ausnahme Maßnahmen zu ergreifen
catch
, darin, die betreffende Aktion auszuführen und dann erneut auszuführen, obwohl er weiß, dass die Ausnahme nicht behoben werden kannthrow
. Ein anderer möglicher Ansatz, wenn es dem Code egal ist, welche Ausnahme ausgelöst wird, ist die Verwendung einesok
Flags mit einemtry/finally
Block. Setzen Sie dasok
Flag auffalse
vor dem Block undtrue
vor dem Beenden des Blocks und vor allenreturn
Blöcken innerhalb des Blocks. Nehmen Sie dann innerhalb anfinally
, dassok
eine Ausnahme aufgetreten sein muss , wenn sie nicht festgelegt ist. Ein solcher Ansatz ist semantisch besser als eincatch
/throw
, aber hässlich und weniger wartbar als er sein sollte.quelle
Dies kann nützlich sein, wenn Ihre Programmierung für eine Bibliothek oder DLL funktioniert.
Diese Rethrow-Struktur kann verwendet werden, um den Aufrufstapel gezielt zurückzusetzen, sodass Sie die Ausnahme von der Funktion selbst erhalten, anstatt die von einer einzelnen Funktion innerhalb der Funktion ausgelöste Ausnahme zu sehen.
Ich denke, dies wird nur verwendet, damit die ausgelösten Ausnahmen sauberer sind und nicht in die "Wurzeln" der Bibliothek gehen.
quelle
Ein möglicher Grund für Catch-Throw besteht darin, Ausnahmefilter, die tiefer im Stapel liegen, daran zu hindern, nach unten zu filtern ( zufälliger alter Link ). Aber wenn das die Absicht wäre, würde es dort natürlich einen Kommentar geben, der dies sagt.
quelle
Es hängt davon ab, was Sie im catch-Block tun und ob Sie den Fehler an den aufrufenden Code weitergeben möchten oder nicht.
Sie könnten sagen
Catch io.FileNotFoundExeption ex
und dann einen alternativen Dateipfad oder einen ähnlichen verwenden, aber trotzdem den Fehler auslösen.Wenn Sie dies
Throw
stattdessen tun,Throw Ex
können Sie die vollständige Stapelverfolgung beibehalten. Throw ex startet den Stack-Trace über die throw-Anweisung neu (ich hoffe, das macht Sinn).quelle
Während viele der anderen Antworten gute Beispiele dafür liefern, warum Sie möglicherweise eine Ausnahme erneut auslösen möchten, scheint niemand ein "endgültiges" Szenario erwähnt zu haben.
Ein Beispiel hierfür ist, dass Sie eine Methode haben, bei der Sie den Cursor setzen (z. B. auf einen Wartecursor), die Methode mehrere Austrittspunkte hat (z. B. if () return;) und Sie möchten sicherstellen, dass der Cursor am zurückgesetzt wird Ende der Methode.
Dazu können Sie den gesamten Code in try / catch / finally einbinden. Stellen Sie den Cursor schließlich wieder auf den rechten Cursor. Damit Sie keine gültigen Ausnahmen begraben, werfen Sie sie erneut in den Haken.
quelle
catch
ein obligatorischer Teil vontry...finally
historisch oder spielt es in diesem Beispiel eine funktionale Rolle? - Ich habe es nur noch einmal überprüft und kann es überhaupttry {} finally {}
ohne den Fangblock verwenden.In dem Beispiel in dem Code, den Sie gepostet haben, macht es in der Tat keinen Sinn, die Ausnahme abzufangen, da nichts an dem Fang getan wird, der nur erneut angezeigt wird. Tatsächlich schadet es mehr als es nützt, wenn der Aufrufstapel verloren geht .
Sie würden jedoch eine Ausnahme abfangen, um im Falle einer Ausnahme eine Logik auszuführen (z. B. das Schließen der SQL-Verbindung der Dateisperre oder nur eine Protokollierung), und diese zurück in den aufrufenden Code zu werfen, um damit umzugehen. Dies ist in einer Geschäftsschicht häufiger als im Front-End-Code, da der Codierer, der Ihre Geschäftsschicht implementiert, möglicherweise die Ausnahme behandelt.
Es ist NICHT sinnvoll, die Ausnahme in dem von Ihnen geposteten Beispiel abzufangen. Mach das nicht so!
quelle
Entschuldigung, aber viele Beispiele als "verbessertes Design" riechen immer noch schrecklich oder können extrem irreführend sein. Nach dem Versuch {} catch {log; werfen} ist einfach völlig sinnlos. Die Ausnahmeprotokollierung sollte an zentraler Stelle in der Anwendung erfolgen. Ausnahmen sprudeln ohnehin in die Stapelverfolgung. Warum nicht irgendwo in der Nähe der Systemgrenzen protokollieren?
Vorsicht ist geboten, wenn Sie Ihren Kontext (dh DTO in einem Beispiel) nur in die Protokollnachricht serialisieren. Es kann leicht vertrauliche Informationen enthalten, die möglicherweise nicht in die Hände aller Personen gelangen sollen, die auf die Protokolldateien zugreifen können. Und wenn Sie der Ausnahme keine neuen Informationen hinzufügen, sehe ich den Punkt der Ausnahmeumhüllung wirklich nicht. Gutes altes Java hat einen Sinn dafür, es erfordert, dass der Aufrufer weiß, welche Art von Ausnahmen man erwarten sollte, wenn er den Code aufruft. Da Sie dies in .NET nicht haben, hilft das Umschließen in mindestens 80% der Fälle, die ich gesehen habe, nichts.
quelle
Zusätzlich zu dem, was die anderen gesagt haben, siehe meine Antwort auf eine verwandte Frage, die zeigt, dass das Fangen und erneutes Werfen kein No-Op ist (es ist in VB, aber ein Teil des Codes könnte C # sein, das von VB aufgerufen wird).
quelle
Die meisten Antworten sprechen von Szenario-Catch-Log-Rethrow.
Anstatt es in Ihren Code zu schreiben, sollten Sie AOP verwenden, insbesondere Postsharp.Diagnostic.Toolkit mit OnExceptionOptions IncludeParameterValue und IncludeThisArgument
quelle
Das erneute Auslösen von Ausnahmen über
throw
ist nützlich, wenn Sie keinen bestimmten Code zur Behandlung aktueller Ausnahmen haben oder wenn Sie eine Logik zur Behandlung bestimmter Fehlerfälle haben, aber alle anderen überspringen möchten.Beispiel:
Es gibt jedoch auch eine andere Möglichkeit , Bedingungsklauseln in catch-Blöcken zu verwenden:
quelle