try - eigene exception c#




Warum eine Ausnahme in C#fangen und erneut auslösen? (10)

Ist das nicht genau so, dass Ausnahmen überhaupt nicht behandelt werden?

Nicht genau, es ist nicht dasselbe. Es setzt den Stacktrace der Exception zurück. Obwohl ich stimme zu, dass dies wahrscheinlich ein Fehler ist und somit ein Beispiel für schlechten Code.

Ich betrachte den Artikel C # - Data Transfer Object auf serialisierbaren DTOs.

Der Artikel enthält dieses Stück 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 (zu einem Noob), aber dieser try-catch-throw löst eine WtfException aus ... Ist das nicht genau so, als würde man überhaupt keine Ausnahmen 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 fast dasselbe wie Java (minus geprüfte Ausnahmen), oder? ... Das heißt, beide haben C ++ verfeinert.

Die Stack Overflow-Frage Der Unterschied zwischen dem Zurückwerfen von parameterlosem Catch und dem Nicht-Tun von irgendetwas? scheint meine Behauptung zu stützen, dass Try-Catch-Throw ein No-Op ist.

BEARBEITEN:

Zusammenfassend für alle, 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 für die Identifizierung der Ursache des Problems sein!

MACHEN

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:


C # (vor C # 6) unterstützt CIL "gefilterte Ausnahmen" nicht, was VB tut, daher ist in C # 1-5 ein Grund für das erneute Auslösen einer Ausnahme, dass Sie nicht genügend Informationen zum Zeitpunkt catch () haben um festzustellen, ob Sie die Ausnahme tatsächlich abfangen wollten.

Zum Beispiel in VB können Sie tun

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

... die MyExceptions mit unterschiedlichen ErrorCode-Werten nicht behandeln würde. In C # vor Version 6 müssten Sie die MyException abfangen und erneut ausgeben, wenn der ErrorCode nicht 123 lautet:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

Seit C # 6.0 können Sie genau wie bei VB filtern :

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}

Du willst nicht ex - werfen, da dies den Call Stack verlieren wird. Siehe Ausnahmebehandlung (MSDN).

Und ja, der try ... catch tut nichts nützliches (außer den Call Stack zu verlieren - also ist es eigentlich noch schlimmer - es sei denn, Sie wollten diese Information aus irgendeinem Grund nicht preisgeben).


Ein Punkt, den die Leute nicht erwähnt haben, ist, dass obwohl .NET-Sprachen nicht wirklich eine richtige Unterscheidung machen, die Frage, ob man eine Aktion ausführen soll , wenn eine Ausnahme auftritt, und ob man sie lösen wird, tatsächlich unterschiedliche Fragen sind. Es gibt viele Fälle, in denen man auf der Grundlage von Ausnahmen vorgehen sollte, die man nicht auflösen kann, und es gibt Fälle, in denen das Auslösen einer Ausnahme nur erforderlich ist, um den Stapel auf einen bestimmten Punkt zu bringen - keine weiteren Maßnahmen erforderlich .

Wegen der allgemeinen Weisheit, dass man Dinge, die man "handhaben" kann, nur "fangen" kann, tut das nicht viel Code, der handeln sollte, wenn Ausnahmen auftreten. Zum Beispiel wird viel Code eine Sperre erhalten, das geschützte Objekt "vorübergehend" in einen Zustand versetzen, der seine Invarianten verletzt, dann wird das Objekt in einen legitimen Zustand versetzt und die Sperre wieder freigegeben, bevor jemand anderes das Objekt sehen kann. Wenn eine Ausnahme auftritt, während sich das Objekt in einem gefährlich ungültigen Zustand befindet, ist es üblich, die Sperre aufzuheben, während sich das Objekt noch in diesem Status befindet. Ein viel besseres Muster wäre, eine Ausnahme zu haben, die auftritt, während das Objekt in einem "gefährlichen" Zustand ist, die Sperre ausdrücklich ungültig, so dass jeder zukünftige Versuch, es zu erwerben, sofort fehlschlagen wird. Die konsequente Verwendung eines solchen Musters würde die Sicherheit der sogenannten "Pokemon" -Ausnahmebehandlung erheblich verbessern, was IMHO einen schlechten Ruf vor allem aufgrund von Code verschafft, der es ermöglicht, Ausnahmen zu durchdringen, ohne vorher entsprechende Maßnahmen zu ergreifen.

In den meisten .NET-Sprachen besteht die einzige Möglichkeit für Code, basierend auf einer Ausnahme zu handeln catch darin, ihn abzufangen (obwohl er weiß, dass er die Ausnahme nicht auflösen wird), die betreffende Aktion auszuführen und dann erneut zu throw ). Ein weiterer möglicher Ansatz, wenn Code sich nicht darum kümmert, welche Ausnahme ausgelöst wird, ist die Verwendung eines ok Flags mit einem try/finally Block; setze das ok Flag vor dem Block auf " false vor dem Verlassen des Blocks auf " true und vor jeder return , die innerhalb des Blocks liegt. Dann nehmen wir an, dass, wenn ok nicht gesetzt ist, eine Ausnahme eingetreten sein muss. Ein solcher Ansatz ist semantisch besser als ein catch / throw , ist aber hässlich und weniger pflegeleicht als er sein sollte.


Ein möglicher Grund für den "catch-throw" ist das Deaktivieren von Ausnahmefiltern, die den Stapel nach unten filtern ( zufälliger alter Link ). Aber natürlich, wenn das beabsichtigt wäre, würde es dort einen Kommentar geben, der das sagt.


Es hängt davon ab, was Sie im catch-Block tun und ob Sie den Fehler an den aufrufenden Code weitergeben wollen oder nicht.

Sie könnten Catch io.FileNotFoundExeption ex sagen und dann einen alternativen Dateipfad oder Catch io.FileNotFoundExeption ex verwenden, aber den Fehler trotzdem Catch io.FileNotFoundExeption ex .

Wenn Sie Throw anstelle von Throw Ex können Sie den gesamten Stack-Trace beibehalten. Throw ex startet den Stack-Trace neu von der throw-Anweisung (ich hoffe, das macht Sinn).


Mein Hauptgrund für Code wie:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

ist so, dass ich einen Haltepunkt im Fang haben kann, der ein instanziiertes Ausnahmeobjekt hat. Ich mache das viel während der Entwicklung / Debugging. Natürlich gibt mir der Compiler eine Warnung über alle unbenutzten e, und idealerweise sollten sie vor einem Release-Build entfernt werden.

Sie sind aber beim Debuggen nett.


Sorry, aber viele Beispiele wie "verbessertes Design" riechen immer noch schrecklich oder können extrem irreführend sein. Versuchen Sie {} catch {log; werfen} ist absolut sinnlos. Die Protokollierung von Ausnahmen sollte an zentraler Stelle innerhalb der Anwendung erfolgen. Ausnahmen bringen den Stacktrace trotzdem zum platzen, warum loggen Sie sie nicht irgendwo in der Nähe der Systemgrenzen auf?

Vorsicht sollte verwendet werden, wenn Sie Ihren Kontext (dh DTO in einem gegebenen Beispiel) nur in die Protokollnachricht serialisieren. Es kann leicht vertrauliche Informationen enthalten, die 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 wirklich nicht den Punkt, an dem die Ausnahme umbrochen wird. Gutes altes Java hat dafür einen Sinn, 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, macht das Wrapping in mindestens 80% der Fälle, die ich gesehen habe, keinen Sinn.


Während viele der anderen Antworten gute Beispiele dafür liefern, warum Sie vielleicht eine Ausnahme wiederholen möchten, scheint niemand ein "endlich" -Szenario erwähnt zu haben.

Ein Beispiel dafür ist, dass Sie eine Methode haben, bei der Sie den Cursor (z. B. auf einen Wartecursor) setzen, die Methode mehrere Exitpunkte hat (z. B. if () return;) und sicherstellen möchten, dass der Cursor auf die zurückgesetzt wird Ende der Methode.

Um dies zu tun, können Sie den gesamten Code in try / catch / finally einpacken. Setzen Sie den Cursor schließlich wieder auf den rechten Cursor. Damit Sie keine gültigen Ausnahmen begraben, werfen Sie es erneut in den Fang.

try
{
    Cursor.Current = Cursors.WaitCursor;
    // Test something
    if (testResult) return;
    // Do something else
}
catch
{
    throw;
}
finally
{
     Cursor.Current = Cursors.Default;
}

Zuerst; die Art, wie der Code in dem Artikel es tut, ist böse. throw ex wird den Aufruf-Stack in der Ausnahme zu dem Punkt zurücksetzen, wo diese throw-Anweisung ist; Verlieren 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 genauso gut (oder, angesichts des throw ex bit, sogar noch besser) ohne den try-catch.

Es gibt jedoch Fälle, in denen Sie eine Ausnahme abfangen und erneut auslösen möchten. Protokollierung könnte einer von ihnen sein:

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}




try-catch