Ich bin nicht in der Lage zu entscheiden, wie Ausnahmen in meiner Anwendung behandelt werden sollen.
Viel, wenn meine Probleme mit Ausnahmen darin bestehen, 1) über einen Remote-Dienst auf Daten zuzugreifen oder 2) ein JSON-Objekt zu deserialisieren. Leider kann ich für keine dieser Aufgaben einen Erfolg garantieren (Netzwerkverbindung trennen, fehlerhaftes JSON-Objekt, das außerhalb meiner Kontrolle liegt).
Wenn ich auf eine Ausnahme stoße, fange ich sie einfach in der Funktion ab und gebe FALSE an den Aufrufer zurück. Meine Logik ist, dass der Anrufer sich nur darum kümmert, ob die Aufgabe erfolgreich war und nicht, warum sie nicht erfolgreich war.
Hier ist ein Beispielcode (in JAVA) einer typischen Methode.
public boolean doSomething(Object p_somthingToDoOn)
{
boolean result = false;
try{
// if dirty object then clean
doactualStuffOnObject(p_jsonObject);
//assume success (no exception thrown)
result = true;
}
catch(Exception Ex)
{
//don't care about exceptions
Ex.printStackTrace();
}
return result;
}
Ich denke, dieser Ansatz ist in Ordnung, aber ich bin wirklich neugierig, welche Best Practices für die Verwaltung von Ausnahmen gelten (sollte ich eine Ausnahme wirklich bis zum Ende eines Aufrufstapels sprudeln lassen?).
Zusammenfassend zu den wichtigsten Fragen:
- Ist es in Ordnung, Ausnahmen nur abzufangen, aber nicht in die Luft zu sprudeln oder das System offiziell zu benachrichtigen (entweder über ein Protokoll oder eine Benachrichtigung an den Benutzer)?
- Welche Best Practices gibt es für Ausnahmen, die nicht dazu führen, dass alles einen Try / Catch-Block erfordert?
Follow Up / Edit
Vielen Dank für das Feedback und einige hervorragende Quellen zum Online-Ausnahmemanagement:
- Best Practices für die Ausnahmebehandlung | O'Reilly Media
- Best Practices für die Ausnahmebehandlung in .NET
- Best Practices: Ausnahmemanagement (Artikel verweist jetzt auf die Kopie von archive.org)
- Antipatterns zur Ausnahmebehandlung
Es scheint, dass das Ausnahmemanagement eines der Dinge ist, die je nach Kontext variieren. Vor allem aber sollte man konsistent sein, wie Ausnahmen innerhalb eines Systems verwaltet werden.
Achten Sie außerdem auf Code-Rot durch übermäßiges Ausprobieren oder Nicht-Respektieren einer Ausnahme (eine Ausnahme warnt das System, was muss noch gewarnt werden?).
Dies ist auch ein ziemlich guter Kommentar von m3rLinEz .
Ich stimme Anders Hejlsberg und Ihnen eher zu, dass es den meisten Anrufern nur wichtig ist, ob die Operation erfolgreich ist oder nicht.
Aus diesem Kommentar ergeben sich einige Fragen, über die beim Umgang mit Ausnahmen nachgedacht werden muss:
- Was ist der Punkt, an dem diese Ausnahme ausgelöst wird?
- Wie macht es Sinn, damit umzugehen?
- Interessiert sich der Anrufer wirklich für die Ausnahme oder interessiert es ihn nur, ob der Anruf erfolgreich war?
- Ist es sinnvoll, einen Anrufer zu zwingen, eine potenzielle Ausnahme zu verwalten?
- Sind Sie respektvoll gegenüber den Redewendungen der Sprache?
- Müssen Sie wirklich ein Erfolgsflag wie boolean zurückgeben? Die Rückgabe von Booleschen Werten (oder Int) ist eher eine C-Denkweise als eine Java-Denkweise (in Java würden Sie nur die Ausnahme behandeln).
- Befolgen Sie die mit der Sprache verknüpften Fehlerverwaltungskonstrukte :)!
quelle
Antworten:
Es scheint mir seltsam, dass Sie Ausnahmen abfangen und in Fehlercodes umwandeln möchten. Warum würde der Anrufer Ihrer Meinung nach Fehlercodes Ausnahmen vorziehen, wenn letzterer sowohl in Java als auch in C # die Standardeinstellung ist?
Wie für Ihre Fragen:
quelle
Dies hängt von der Anwendung und der Situation ab. Wenn Sie eine Bibliothekskomponente erstellen, sollten Sie Ausnahmen in die Luft sprudeln lassen, obwohl diese so verpackt werden sollten, dass sie mit Ihrer Komponente kontextbezogen sind. Wenn Sie beispielsweise eine XML-Datenbank erstellen und beispielsweise das Dateisystem zum Speichern Ihrer Daten verwenden und zum Sichern der Daten Dateisystemberechtigungen verwenden. Sie möchten keine FileIOAccessDenied-Ausnahme auslösen, da dadurch Ihre Implementierung verloren geht. Stattdessen würden Sie die Ausnahme umbrechen und einen AccessDenied-Fehler auslösen. Dies gilt insbesondere dann, wenn Sie die Komponente an Dritte vertreiben.
Ob es in Ordnung ist, Ausnahmen zu schlucken. Das hängt von Ihrem System ab. Wenn Ihre Anwendung die Fehlerfälle behandeln kann und es keinen Vorteil hat, den Benutzer darüber zu informieren, warum sie fehlgeschlagen ist, fahren Sie fort, obwohl ich dringend empfehle, dass Sie den Fehler protokollieren. Ich fand es immer frustrierend, aufgerufen zu werden, um ein Problem zu beheben und festzustellen, dass sie die Ausnahme verschluckt haben (oder sie ersetzt und stattdessen eine neue ausgelöst haben, ohne die innere Ausnahme zu setzen).
Im Allgemeinen verwende ich die folgenden Regeln:
Ich finde den folgenden Code ein Geruch:
Code wie dieser hat keinen Sinn und sollte nicht enthalten sein.
quelle
// do something
irgendwelche enthälttry/finally
Blöcke , die um den Punkt führt, diefinally
werden Blöcke vor dem Ausführencatch
Block. Ohne dastry/catch
fliegt die Ausnahme bis zum oberen Rand des Stapels, ohne dassfinally
Blöcke ausgeführt werden. Auf diese Weise kann der Handler der obersten Ebene entscheiden, ob diefinally
Blöcke ausgeführt werden sollen oder nicht .Ich möchte eine weitere gute Quelle zu diesem Thema empfehlen. Es ist ein Interview mit den Erfindern von C # und Java, Anders Hejlsberg und James Gosling, zum Thema Javas Checked Exception.
Fehler und Ausnahmen
Es gibt auch großartige Ressourcen am Ende der Seite.
Ich stimme Anders Hejlsberg und Ihnen eher zu, dass es den meisten Anrufern nur wichtig ist, ob die Operation erfolgreich ist oder nicht.
BEARBEITEN: Weitere Details zur Konvertierung hinzugefügt
quelle
FetchData
und eine Ausnahme eines unerwarteten Typs auslöst, kann er nicht wissen, ob diese Ausnahme lediglich bedeutet, dass die Daten nicht verfügbar sind (in diesem Fall würde die Fähigkeit des Codes, ohne sie auszukommen, sie "auflösen"), oder ob dies bedeutet, dass die CPU in Flammen steht und das System bei der ersten Gelegenheit eine "Sicherheitsabschaltung" durchführen sollte. Es hört sich so an, als würde Herr Hejlsberg vorschlagen, dass der Code den ersteren annehmen sollte; Vielleicht ist das angesichts der bestehenden Ausnahmehierarchien die bestmögliche Strategie, aber es scheint trotzdem schwierig zu sein.Überprüfte Ausnahmen sind ein kontroverses Thema im Allgemeinen und in Java im Besonderen (später werde ich versuchen, einige Beispiele für diejenigen zu finden, die dafür und dagegen sind).
Als Faustregeln sollte die Ausnahmebehandlung in keiner bestimmten Reihenfolge um diese Richtlinien herumgehen:
printStackTrace()
oder ähnliches, es besteht die Möglichkeit, dass einer Ihrer Benutzer irgendwann eine dieser Stapelspuren erhält und genau keine Kenntnisse darüber hat, was er damit tun soll.Exception
ist sehr wahrscheinlich, dass Sie ansonsten wichtige Ausnahmen verschlucken.Error
s fangen !! , was bedeutet: Fange niemalsThrowable
s, daError
s Unterklassen der letzteren sind.Error
s sind Probleme, die Sie höchstwahrscheinlich nie lösen können (z. B.OutOfMemory
oder andere JVM-Probleme).Stellen Sie in Bezug auf Ihren speziellen Fall sicher, dass jeder Client, der Ihre Methode aufruft, den richtigen Rückgabewert erhält. Wenn etwas fehlschlägt, gibt eine boolesche Rückgabemethode möglicherweise false zurück. Stellen Sie jedoch sicher, dass die Stellen, an denen Sie diese Methode aufrufen, damit umgehen können.
quelle
Sie sollten nur die Ausnahmen abfangen, mit denen Sie umgehen können. Wenn Sie beispielsweise über ein Netzwerk lesen und die Verbindung abläuft und eine Ausnahme auftritt, können Sie es erneut versuchen. Wenn Sie jedoch über ein Netzwerk lesen und eine IndexOutOfBounds-Ausnahme erhalten, können Sie damit wirklich nicht umgehen, da Sie nicht wissen, was die Ursache ist. Wenn Sie false oder -1 oder null zurückgeben, stellen Sie sicher, dass es sich um bestimmte Ausnahmen handelt. Ich möchte nicht, dass eine Bibliothek, die ich verwende, in einem Netzwerk einen Lesevorgang zurückgibt, wenn die Ausnahme darin besteht, dass der Heap nicht über genügend Speicher verfügt.
quelle
Ausnahmen sind Fehler, die nicht Teil der normalen Programmausführung sind. Abhängig davon, was Ihr Programm tut und wie es verwendet wird (dh ein Textverarbeitungsprogramm oder ein Herzmonitor), möchten Sie verschiedene Dinge tun, wenn Sie auf eine Ausnahme stoßen. Ich habe mit Code gearbeitet, der Ausnahmen als Teil der normalen Ausführung verwendet, und es ist definitiv ein Codegeruch.
Ex.
Dieser Code macht mich barf. IMO sollten Sie keine Ausnahmen wiederherstellen, es sei denn, es ist ein kritisches Programm. Wenn Sie Ausnahmen werfen, passieren schlimme Dinge.
quelle
All dies scheint vernünftig zu sein, und häufig hat Ihr Arbeitsplatz möglicherweise eine Richtlinie. Bei uns haben wir Ausnahmetypen definiert:
SystemException
(nicht markiert) undApplicationException
(aktiviert).Wir haben uns darauf geeinigt, dass
SystemException
es unwahrscheinlich ist, dass s wiederhergestellt werden können, und dass sie einmal an der Spitze behandelt werden. Zur weiteren Zusammenhang auch unsereSystemException
sind s exteneded um anzuzeigen , wo sie aufgetreten ist , zBRepositoryException
,ServiceEception
usw.ApplicationException
s könnte eine geschäftliche Bedeutung habenInsufficientFundsException
und sollte vom Client-Code behandelt werden.Ohne ein konkretes Beispiel ist es schwierig, Ihre Implementierung zu kommentieren, aber ich würde niemals Rückkehrcodes verwenden, sie sind ein Wartungsproblem. Sie können eine Ausnahme verschlucken, müssen jedoch entscheiden, warum und das Ereignis und die Stapelverfolgung immer protokollieren. Da Ihre Methode keine andere Verarbeitung hat, ist sie ziemlich redundant (außer für die Kapselung?) Und
doactualStuffOnObject(p_jsonObject);
könnte daher einen Booleschen Wert zurückgeben!quelle
Nach einigem Nachdenken und Betrachten Ihres Codes scheint es mir, dass Sie die Ausnahme einfach als Booleschen Wert erneut auslösen. Sie können die Methode diese Ausnahme einfach durchlaufen lassen (Sie müssen sie nicht einmal abfangen) und sie im Aufrufer behandeln, da dies der Ort ist, an dem es darauf ankommt. Wenn die Ausnahme den Aufrufer veranlasst, diese Funktion erneut zu versuchen, sollte der Anrufer derjenige sein, der die Ausnahme abfängt.
Es kann vorkommen, dass die Ausnahme, auf die Sie stoßen, für den Anrufer keinen Sinn ergibt (dh es handelt sich um eine Netzwerkausnahme). In diesem Fall sollten Sie sie in eine domänenspezifische Ausnahme einschließen.
Wenn andererseits die Ausnahme einen nicht behebbaren Fehler in Ihrem Programm signalisiert (dh das endgültige Ergebnis dieser Ausnahme ist die Programmbeendigung), möchte ich dies persönlich explizit machen, indem ich sie abfange und eine Laufzeitausnahme auslöse.
quelle
Wenn Sie das Codemuster in Ihrem Beispiel verwenden möchten, nennen Sie es TryDoSomething und fangen Sie nur bestimmte Ausnahmen ab.
Erwägen Sie auch die Verwendung eines Ausnahmefilters, wenn Sie Ausnahmen für Diagnosezwecke protokollieren. VB bietet Sprachunterstützung für Ausnahmefilter. Der Link zu Greggms Blog enthält eine Implementierung, die von C # aus verwendet werden kann. Ausnahmefilter haben bessere Eigenschaften für die Debugbarkeit gegenüber Catch und Rethrow. Insbesondere können Sie das Problem im Filter protokollieren und die Ausnahme weiter verbreiten lassen. Mit dieser Methode kann ein JIT-Debugger (Just in Time) den vollständigen Originalstapel anhängen. Ein erneuter Wurf schneidet den Stapel an der Stelle ab, an der er erneut geworfen wurde.
Die Fälle, in denen TryXXXX sinnvoll ist, sind, wenn Sie eine Drittanbieterfunktion einschließen, die Fälle auslöst, die nicht wirklich außergewöhnlich sind oder einfach nur schwer zu testen sind, ohne die Funktion aufzurufen. Ein Beispiel wäre so etwas wie:
Ob Sie ein Muster wie TryXXX verwenden oder nicht, ist eher eine Stilfrage. Die Frage, alle Ausnahmen zu fangen und zu schlucken, ist kein Stilproblem. Stellen Sie sicher, dass sich unerwartete Ausnahmen verbreiten dürfen!
quelle
Ich schlage vor, Ihre Hinweise aus der Standardbibliothek für die von Ihnen verwendete Sprache zu übernehmen. Ich kann nicht für C # sprechen, aber schauen wir uns Java an.
Zum Beispiel hat java.lang.reflect.Array eine statische
set
Methode:Der C-Weg wäre
... wobei der Rückgabewert ein Erfolgsindikator ist. Aber du bist nicht mehr in der C-Welt.
Sobald Sie Ausnahmen akzeptieren, sollten Sie feststellen, dass dies Ihren Code einfacher und klarer macht, indem Sie Ihren Fehlerbehandlungscode von Ihrer Kernlogik entfernen. Ziel ist es, viele Anweisungen in einem einzigen
try
Block zu haben.Wie andere angemerkt haben, sollten Sie in der Art der Ausnahme, die Sie fangen, so genau wie möglich sein.
quelle
Wenn Sie eine Ausnahme abfangen und false zurückgeben, sollte dies eine sehr spezifische Ausnahme sein. Du machst das nicht, du fängst sie alle und gibst falsch zurück. Wenn ich eine MyCarIsOnFireException bekomme, möchte ich sofort davon erfahren! Die restlichen Ausnahmen interessieren mich vielleicht nicht. Sie sollten also einen Stapel von Ausnahmebehandlungsroutinen haben, die für einige Ausnahmen "whoa whoa, hier stimmt etwas nicht" sagen (erneut werfen oder eine neue Ausnahme abfangen und erneut werfen, die besser erklärt, was passiert ist) und für andere einfach false zurückgeben.
Wenn dies ein Produkt ist, das Sie starten werden, sollten Sie diese Ausnahmen irgendwo protokollieren, damit Sie die Dinge in Zukunft optimieren können.
Bearbeiten: Was die Frage betrifft, alles in einen Versuch / Fang zu packen, denke ich, dass die Antwort ja ist. Ausnahmen sollten in Ihrem Code so selten sein, dass der Code im catch-Block so selten ausgeführt wird, dass er die Leistung überhaupt nicht beeinträchtigt. Eine Ausnahme sollte ein Zustand sein, in dem Ihre Zustandsmaschine kaputt gegangen ist und nicht weiß, was zu tun ist. Zumindest eine Ausnahme erneut auslösen, die erklärt, was zu der Zeit geschah, und die gefangene Ausnahme enthält. "Ausnahme in der Methode doSomeStuff ()" ist nicht sehr hilfreich für alle, die herausfinden müssen, warum es im Urlaub (oder bei einem neuen Job) kaputt gegangen ist.
quelle
Meine Strategie:
Wenn die ursprüngliche Funktion void zurückgibt, ändere ich sie in bool . Wenn eine Ausnahme / ein Fehler aufgetreten ist, geben Sie false zurück . Wenn alles in Ordnung war, geben Sie true zurück .
Wenn die Funktion etwas zurückgeben soll, geben Sie bei Auftreten einer Ausnahme / eines Fehlers null zurück , andernfalls das zurückgegebene Element.
Anstatt einen String zu boolen zurückgegeben werden, die die Beschreibung des Fehlers enthält.
In jedem Fall protokollieren Sie den Fehler, bevor Sie etwas zurückgeben.
quelle
Einige ausgezeichnete Antworten hier. Ich möchte hinzufügen, dass, wenn Sie am Ende etwas haben, das Sie gepostet haben, zumindest mehr als die Stapelverfolgung gedruckt werden. Sagen Sie, was Sie zu der Zeit getan haben, und Ex.getMessage (), um dem Entwickler eine Chance zum Kampf zu geben.
quelle
Try / Catch-Blöcke bilden einen zweiten Satz von Logik, der über den ersten (Haupt-) Satz eingebettet ist. Als solche sind sie eine großartige Möglichkeit, unlesbaren, schwer zu debuggenden Spaghetti-Code zu entfernen.
Vernünftigerweise wirken sie bei der Lesbarkeit Wunder, aber Sie sollten nur zwei einfache Regeln befolgen:
Verwenden Sie sie (sparsam) auf niedriger Ebene, um Probleme bei der Bibliotheksbehandlung zu erkennen und sie wieder in den logischen Hauptfluss zu streamen. Der größte Teil der von uns gewünschten Fehlerbehandlung sollte aus dem Code selbst als Teil der Daten selbst stammen. Warum besondere Bedingungen stellen, wenn die zurückgegebenen Daten keine besonderen sind?
Verwenden Sie einen großen Handler auf der höheren Ebene, um einige oder alle seltsamen Bedingungen im Code zu verwalten, die nicht auf einer niedrigeren Ebene abgefangen werden. Machen Sie etwas Nützliches mit den Fehlern (Protokolle, Neustarts, Wiederherstellungen usw.).
Abgesehen von diesen beiden Arten der Fehlerbehandlung sollte der gesamte Rest des Codes in der Mitte frei und frei von Try / Catch-Code und Fehlerobjekten sein. Auf diese Weise funktioniert es einfach und wie erwartet, egal wo Sie es verwenden oder was Sie damit machen.
Paul.
quelle
Ich bin vielleicht etwas spät dran mit der Antwort, aber die Fehlerbehandlung können wir immer ändern und im Laufe der Zeit weiterentwickeln. Wenn Sie mehr über dieses Thema lesen möchten, habe ich in meinem neuen Blog einen Beitrag darüber geschrieben. http://taoofdevelopment.wordpress.com
Viel Spaß beim Codieren.
quelle