2010-10-20 6 views
8

Warum ist es vorzuziehen, diese AusnahmeWas ist der Zweck, bestimmte Exception Subclasses zu werfen?

Throw New DivideByZeroException("You can't divide by zero") 

über dieses allgemeinen eine werfen:

Throw New Exception("You can't divide by zero") 

Welchen Vorteil in diesem Beispiel gewonnen wird? Die Nachricht sagt schon alles. Haben Standard-Unterklassen, die von der Basis-Ausnahmeklasse erben, jemals andere Methoden als die Basis? Ich habe keinen Fall gesehen, aber ich muss zugeben, dass ich dazu tendiere, die Basis Exception zu werfen.

+0

Da Sie bestimmte Ausnahmen abfangen können, können Sie mehrere catch-Anweisungen in einem try ... catch-Block haben. – Nate

+0

"Haben Standardunterklassen, die von der Basis-Exception-Klasse erben, jemals unterschiedliche Methoden als die Basis?": Ja, aber die meisten Antworten haben sich nicht auf diesen Punkt konzentriert. Ein einfaches Beispiel hierfür finden Sie unter WebException: http://msdn.microsoft.com/en-us/library/system.net.webexception.aspx. Beachten Sie insbesondere die Eigenschaft "Response". – Brian

Antwort

20

Der Typ der Ausnahme ermöglicht es den Handlern der Ausnahme, sie zu filtern. Wenn alle, die Sie geworfen haben, Ausnahmen vom Typ Exception waren, wie würden die Handler wissen, welche Ausnahmen zu fangen sind und welche es erlauben sollen, den Call-Stack weiter zu verbreiten?

Zum Beispiel, wenn Sie werfen immer Exception:

void Foo(string item) { 
    try { 
    if (Bar(item)) { 
     Console.WriteLine("BAR!"); 
    } 
    } catch (Exception e) { 
    Console.WriteLine("Something bad?"); 
    } 
} 

bool Bar(string item) { 
    if (item == null) { 
    throw new Exception("Argument is null!"); 
    } 

    return Int32.Parse(item) != 0; 
} 

Wie funktioniert der Anrufer Foo wissen, ob eine Null-Ausnahme aufgetreten ist oder wenn die Int32.Parse() gescheitert? Es muss den Typ der geworfenen Ausnahme überprüfen (oder einen unangenehmen String-Vergleich durchführen).

Es ist noch besorgniserregender, wenn Sie eine ThreadAbortException oder OutOfMemoryException erhalten, die an Stellen auftreten kann, von denen Sie keine Ausnahme erwarten würden. In diesen Fällen, wenn Ihr Fangcode nur Exception fängt, können Sie diese (wichtigen) Ausnahmen maskieren und Ihrem Programm (oder System) Schaden zufügen.

sollte Der Beispielcode lesen:

void Foo(string item) { 
    try { 
    if (Bar(item)) { 
     Console.WriteLine("BAR!"); 
    } 
    } catch (ArgumentNullException ae) { 
    Console.WriteLine("Null strings cannot be passed!"); 
    } catch (FormatException fe) { 
    Console.WriteLine("Please enter a valid integer!"); 
    } 
} 

bool Bar(string item) { 
    if (item == null) { 
    throw new ArgumentNullException("item"); 
    } 

    return Int32.Parse(item) != 0; 
} 
+0

+1, nice one ... –

+0

Um mit dieser Antwort zu gehen: der Message-Parameter sollte eine für Menschen lesbare Zeichenfolge sein. Sicher, Sie könnten es analysieren, wenn Sie eine generische Ausnahme abfangen, aber das ist ein Overhead, den ich nicht mag. Vor allem, wenn sich die Nachricht ändern kann. – Allan

7

Weil du anders unterschiedliche Fehler mehrere catch-Anweisungen und Griff haben kann. Beispielsweise kann eine DivideByZero-Ausnahme den Benutzer auffordern, einen Eintrag zu korrigieren, während eine FileNotFound-Ausnahme einen Benutzer möglicherweise warnt, dass das Programm nicht fortfahren und das Programm schließen kann.

Es gibt eine schöne in ausführlichen Artikel der Beantwortung dieser Frage hier: http://blogs.msdn.com/b/dotnet/archive/2009/02/19/why-catch-exception-empty-catch-is-bad.aspx

5

Anstatt Filterung auf den Text entlang der Fehlerstrom basierend senden, können Sie mehrere Arten von Ausnahmen zu fangen. Jeder kann einen sehr spezifischen Weg haben, eine Wiederherstellung durchzuführen. Der Text dient nur dazu, dem Benutzer oder dem Debugger Feedback zu geben, aber das Programm kümmert sich um den Ausnahmetyp. Aus dem gleichen Grund gibt es Polymorphismus für Benutzer erstellte Klassen, gibt es für Ausnahmen.

Es ist viel einfacher, mehrere catch-Anweisungen für verschiedene Ausnahmetypen einzubeziehen, als den Nachrichtentext zu analysieren, um zu verstehen, was zur korrekten Behandlung des Problems zu tun ist.

+0

plus Parsen des Nachrichtentextes, wenn seine Lokalisierung fast unmöglich wird –

3

Die verschiedenen Unterklassen von Exception tragen semantische Bedeutung - eine ArgumentNullException weist auf ein anderes Problem hin als eines, das eine DivideByZeroException erzeugt, und der Programmierer kann diese Probleme anders behandeln. Darüber hinaus können Unterklassen zusätzliche Eigenschaften oder Methoden definieren, die bei der Diagnose oder Behandlung des Problems helfen können, wenn der Programmierer sie verwendet.

1
try { 
    Do(); 
} 
catch (MyException) 
{ 
    // reaction on MyException 
} 
catch (AnotherException) 
{ 
    // another reaction on AnotherException 
{ 
// SomeException will not be caught 


void Do() 
{ 
    if (...) 
     throw new MyException(); 
    else if (...) 
     throw new AnotherException(); 
    else 
     throw new SomeException(); 
} 
1

In einer gut gestalteten Ausnahme Hierarchie, macht verschiedene Arten von Ausnahmen, die es möglich, catch-Anweisungen zu haben, die unter verschiedenen Umständen unterschiedliche Aktionen ausführen. Idealerweise würde eine Familie von Ausnahmen aus einer Funktion hervorgehen, wenn die bestimmte Aktion nicht abgeschlossen werden konnte, aber Klasseninvarianten, die vor dem Versuch der Aktion hätten gelten sollen, mit Ausnahme der durch den Fehlschlag der Aktion implizierten wahrscheinlich. Beispielsweise sollte eine get-object-Methode einer Auflistung eine Ausnahme aus dieser Familie auslösen, wenn die Sammlung gültig scheint, das angeforderte Objekt jedoch nicht existiert.

Wenn eine Funktion auf eine Weise fehlschlägt, die angibt, dass Klasseninvarianten beim Aufruf nicht gehalten wurden oder nicht mehr bestehen, nachdem sie zurückgegeben wurde, sollte eine Ausnahme aus einer anderen Familie ausgelöst werden. Beachten Sie, dass es für eine Funktion angemessen sein kann, eine Ausnahme von der ersten Familie abzufangen und als zweite erneut auszuführen, wenn die Ausnahme nur dann eintreten würde, wenn Invarianten verletzt würden. Es kann auch gelegentlich angebracht sein, eine Ausnahme des zweiten Typs zu fangen und eine der ersten zu werfen, wenn die Ausnahmen von einem Objekt kommen, das nach der Rückkehr der Funktion niemals benutzt wird.

Wenn die Dinge wirklich schrecklich sind (z. B. OutOfMemoryException, CpuCatchingFireException usw.), sollte dies eine andere Hierarchie als die ersten beiden sein.

Bestehende Ausnahmen folgen nicht diesem Muster, aber man könnte es für alle neuen Ausnahmen verwenden, die man erstellt.

4

Direkt von MSDN - Exception Handling:

Betrachten bestimmte Ausnahmen zu kontrollieren, wenn Sie verstehen, warum es in einem bestimmten Kontext geworfen werden.

Sie sollten nur die Ausnahmen abfangen, von denen Sie wiederherstellen können. Zum Beispiel kann eine FileNotFoundException, die aus einem Versuch resultiert, eine nicht existierende Datei zu öffnen, von einer Anwendung gehandhabt werden, weil sie das Problem an den Benutzer kommunizieren kann und es dem Benutzer erlaubt, einen anderen Dateinamen zu spezifizieren oder die Datei zu erstellen. Eine Anforderung zum Öffnen einer Datei, die eine ExecutionEngineException generiert, sollte nicht behandelt werden, da die zugrunde liegende Ursache der Ausnahme nicht mit einiger Sicherheit bekannt sein kann, und die Anwendung kann nicht sicherstellen, dass es sicher ist, die Ausführung fortzusetzen.

Sie nicht zu viele catch, als eine weitere Ausnahme von innerhalb eines catch-Block zu werfen den Stack-Trace zurückgesetzt und bewirken, dass die wichtige Debug-Informationen verloren, als erneut MSDN schlägt vor:

nicht zu viele tun Fang. Ausnahmen sollten häufig erlaubt werden, sich auf dem Aufrufstapel fortzupflanzen.

Abfangen von Ausnahmen, die Sie nicht legitim behandeln können, verbirgt kritische Debugging-Informationen.

Am Ende eine Ausnahme Fang für den Umgang mit bestimmten Ausnahmen sein sollte, dass Sie unter bestimmten gängiges Szenario auftreten erwarten, wo Sie sich anmelden möchten, oder um ein spezielles Verhalten auf Ausnahme fangen zu haben, sonst einfach throw es, weg wie Eric Lippert selbst empfiehlt auf seinem Blog (siehe Too much reuse Artikel).

try { 
    ... 
} catch (Exception ex) { 
    throw; // This does not reset the stack trace. 
} 

Statt:

try { 
    ... 
} catch (Exception ex) { 
    throw ex; // This does reset the stack trace. 
} 

Schließlich ist ein Exception nicht zwingend notwendig, einige Besonderheiten als ergänzende Eigenschaften oder Methoden oder überhaupt anbieten zu können, ist es der Name es, die für sich selbst spricht aus, so dass Sie filtern Ihren Fang nach einer bestimmten Art von Ausnahme.

EDIT # 1

Ein weiterer interessanter Link über Fehler auf Blog Eric Lippert Handhabung: Vexing exceptions.

3

Eine Ausnahme wird typischerweise entweder (1) abgefangen, protokolliert und erneut geworfen, (2) abgefangen und behandelt oder (3) nicht abgefangen.

Wenn es abgefangen, protokolliert und erneut ausgelöst wird, können wichtige zusätzliche Informationen in einem bestimmten Ausnahmetyp gespeichert werden, der es dem Protokollierungscode ermöglicht, umfangreichere Informationen zu speichern, sodass der Analyst versucht, das Problem zu beheben das verursachte die Ausnahme kann so effizienter.

Wenn es gefangen und behandelt wird, dann müssen Sie wissen, wie Sie mit dem Problem umgehen. Wenn eine Ausnahme von einem bestimmten Typ ist, dann ist das ein großer Hinweis für den Entwickler, der den Handler darüber schreibt, ob er mit der Ausnahme umgehen kann oder nicht. Sie sollten nur mit Ausnahmen umgehen, die Sie erwarten, und wissen, wie Sie sie wiederherstellen können.

Wenn es nicht gefangen wird, dann wird der Prozess untergehen und wahrscheinlich wird irgendwo ein Absturzspeicherauszug gesendet. Jetzt sind wir zurück in Fall (1).

In allen drei Fällen ist mehr Typinformation vorzuziehen, als weniger.