Ich habe viele Male gelesen, dass man niemals blind Ausnahmen fangen sollte. Einige Leute sagen, dass es in Ordnung ist, Main () in einen catch-Block zu packen, um Fehler anzuzeigen und nicht nur zu beenden ( sieh diesen SO-Beitrag zum Beispiel ), aber es scheint einen Konsens zu geben, dass du dein Programm niemals laufen lassen solltest, wenn etwas unerwartetes passiert, da es in einem unbekannten Zustand ist, und kann auf unerwartete Weise handeln.
Obwohl ich der Tatsache zustimme, dass es eher eine falsche Idee ist, Fehler zu verstecken, als sie zu reparieren, bedenken Sie den folgenden Fall:
Sie haben einen riesigen Server. Millionen von Codezeilen.
Wenn es gestartet wird, lädt es den gesamten Kunden in seinen lokalen Cache.
Für mich ist es sehr sinnvoll, dies zu schreiben:
%Vor%Ohne den blinden Haken würde das Versäumen, einen einzelnen Kunden zu laden, nur den gesamten Server zum Absturz bringen.
Ich bevorzuge es, meinen Server mit einem kleinen unbekannten Nebeneffekt bei einem Kunden zu betreiben, anstatt meinen ganzen Server herunterzufahren.
Natürlich, wenn ich meinen Catch-Code einmal ausführe, werde ich als Erstes den Fehler in meinem Code beheben.
Gibt es etwas, das ich hier übersehen habe? Gibt es Best Practices (außer der Strategie "Nie unerwartete Ausnahmen erfassen"?)?
Ist es besser, die Ausnahme in der LoadCustomer () -Methode abzufangen, eine "CustomerLoadException" von dort erneut auszulösen und CustomerLoadException statt aller Exception im aufrufenden Code zu fangen?
Dies ist eine Frage von Robustheit , die auch bei unerklärlichen Fehlern funktioniert, gegenüber Korrektheit , die lieber vollständig ausfallen als unzuverlässige Ergebnisse liefern.
Wenn Sie beispielsweise an lebenserhaltenden Systemen arbeiten, möchten Sie nicht, dass Ihr System aufgrund eines unbekannten Fehlers einfach heruntergefahren wird. Selbst wenn Ihr Zustand nicht gut definiert ist, ist es wahrscheinlich besser, zu operieren, als zu beenden.
Andererseits, wenn Ihr Programm ein Einkaufswagen ist, ist es wahrscheinlich besser, einfach komplett auszufallen, als möglicherweise den falschen Artikel zu senden oder den falschen Leuten den falschen Betrag zu berechnen.
Alles dazwischen ist eine Grauzone und es ist ein Urteilsspruch. In der Hauptsache scheint es so zu sein, dass die Programmierung von Lebenserhaltungssystemen seltener ist als die Programmierung von Einkaufswagen, weshalb der breite Rat den Menschen sagt, was am häufigsten vorkommt, was im Falle eines unerwarteten Fehlers fehlschlägt. Wenn Sie an einem Fall arbeiten, in dem das nicht angemessen ist (wie in Ihrem Serverbeispiel), wissen Sie es wahrscheinlich besser.
"Gibt es etwas, das ich hier übersehen habe?"
Ja.
"Gibt es bekannte Best Practices (außer der Strategie" Nie unerwartete Ausnahmen erfassen "?)"
Ja. Verkapselung. Zuweisung von Verantwortung. Die S. O. L. I. D. Prinzipien.
Ist es besser, die Ausnahme in der LoadCustomer () -Methode abzufangen, eine "CustomerLoadException" von dort erneut auszulösen und CustomerLoadException statt aller Exception im aufrufenden Code zu fangen?
Ja. Dies kapselt alle Dinge zusammen, die in der Klassendefinition, die die Methode loadCustomer
bereitstellt, schiefgehen können.
Ich denke, es hängt vom Senario ab, aber in der Regel erhalte ich nur Ausnahmen, die ich durch den täglichen Gebrauch der App erwarte, und benutze eine Art unbehandeltes Ausnahmereporting / Logging-Tool wie Gesundheitsüberwachung für ASP.Net. Wenn es jedoch ein kritischer Teil der App ist, der einfach keine Ausnahme ohne Ausnahme haben kann, erhalte ich alle Ausnahmen und melde / protokolliere sie erneut.
Es ist eine Sache von Fall zu Fall. Wenn Sie sicherstellen können, dass der Ladefehler keine Auswirkungen auf den Rest der Kunden hat, erscheint es sinnvoll, diesen bestimmten Kunden zu protokollieren und zu überspringen. Nicht alle Ausnahmen funktionieren jedoch so. Speziell für Fehlerbedingungen, zB VirtualMachineError , ist es oft der Falls das Problem den gesamten Programmlauf beeinträchtigt hat und Sie sollten abbrechen.
Ich bevorzuge es, meinen Server mit einem kleinen unbekannten Nebeneffekt bei einem Kunden zu betreiben, anstatt meinen ganzen Server herunterzufahren.
Woher wissen Sie, dass der Bug, der die Ausnahme in LoadCustomer () verursacht hat, nichts anderes abgestreift hat? Im Großen und Ganzen denke ich, dass ich eine "log exception" vorziehen würde, die Ausnahme auf eine höhere Ebene zurückwerfen würde und dort wahrscheinlich exit.
In diesem speziellen Fall könnte ich wahrscheinlich für beide Arten des Umgangs mit ihm argumentieren, aber im Allgemeinen ist der Umgang mit Fehlern, mit denen Sie nicht klar umgehen können, nicht ideal.
Ich denke, das, was Sie vermissen, ist, dass die "Best Practices" für eine Desktop-Anwendung nicht unbedingt dieselben sind wie für einen Server, die nicht mit denen für eine Webseite übereinstimmen.
>Wenn eine Webseite eine unbehandelte Ausnahme auslöst, denken Sie "meh" und klicken auf "Aktualisieren". Wenn eine Desktop-Anwendung eine solche auswirft, verlieren Sie möglicherweise Daten und sind ziemlich genervt. Wenn ein Server einen auswirft, könnte Ihr gesamtes Unternehmen ausfallen, bis es behoben ist.
Es hängt auch davon ab, wie einfach es ist, einen Patch zu installieren. Wenn es Ihnen leicht fällt, einen Patch auf alle Installationen (z. B. Ihren eigenen Server oder eine interne App) zu übertragen, ist es möglicherweise besser, die Exception nicht einzufangen. Wenn Sie etwas schreiben, das nicht gepatcht werden kann (ein Spiel für eine ältere Konsole oder eine eingebettete Geräteanwendung), sollten Sie die Ausnahme vielleicht besser verschlucken.
Natürlich wäre es unter allen Umständen besser, wenn Ihre Tests das Problem auffangen, damit Sie richtig damit umgehen können, aber wir wissen alle, dass Bugs passieren ...
Es ist völlig in Ordnung, Ausnahmen zu erfahren, von denen Sie wissen. Sie könnten in Ihrem Beispiel eine NullObjectException erwarten. Das zu fangen ist völlig in Ordnung.
Wenn Sie eine generische Exception abfangen, sagen Sie im Wesentlichen Folgendes: "Ich weiß nicht, was schiefgelaufen ist, oder ob der Server in Flammen steht und das ist mir egal ." Während Ihr Server möglicherweise weiterläuft, können Sie nicht sagen, was der interne Status sein könnte oder ob diese Ausnahme zu einem Absturz führen wird.
Wenn Sie zu diesem Zeitpunkt beliebige Exceptions abfangen, sind Sie, wenn später ein Absturz auftritt, weiter von der Stelle entfernt, an der der "echte" Fehler aufgetreten ist. Dadurch wird es viel schwieriger, den Fehler zu finden.
Wenngleich das Abfangen einer generischen Ausnahme nicht zu einem sofortigen Absturz führt, ist es eher ein Absturz, der eher früh im Entwicklungszyklus auftritt und leicht behoben werden kann (da Sie wissen, wo der Fehler tatsächlich aufgetreten ist).
Es gibt wirklich keinen Vorteil, eine Ausnahme zu fangen.
Ich bin in diesem Bereich nicht sehr erfahren. Es scheint mir jedoch, dass 1. Es gibt einige Ausnahmen für fast jede Regel. Zu wissen, wann man eine Regel bricht, ist wichtig. 2. Im Falle der Ausnahmebehandlung ist es fast nie eine gute Idee, Ausnahmen blind zu erfassen. Dies liegt daran, dass möglicherweise einige wirklich unerwartete Fehler auftreten.
Zum Beispiel, in Python (mindestens 2.5.2), kann ich blind das ctrl + c-Signal (Linux) abfangen. Das bedeutet, dass ich die Anwendung im Notfall nicht beenden kann.
Okay, lassen Sie mich das so sagen ... sagen, der mysteriöse Kunde konnte nicht geladen werden, weil er nicht existiert, weil jemand herausgefunden hat, wie er die vorderen Schichten Ihres Systems hacken kann. Nun stellen Sie sich vor, der Hacker macht alle Arten von bösartigem Zeug mit der Macht, nicht zu existieren, bis er eine Funktion auslöst, die versucht, von einem unbekannten Wert des Kunden zu lesen, jetzt wird das System trotzdem abstürzen, aber der Hacker wäre in der Lage gewesen alle Arten von Störungen mit dem ungültigen Kunden zu tun.
Ist das wirklich besser?
Sie können alle Ausnahmen (nicht Fehler) erfassen, wenn Sie wissen, welche Objekte und Ressourcen betroffen sein könnten, und alle diese Objekte verwerfen.
In Ihrem Fall können Sie also in Ordnung sein, um mit der Ausnahme umzugehen, die den gesamten Prozess in einen Noop verwandelt. Aber Sie müssen sicher sein, dass: - Es sind keine anderen freigegebenen Ressourcen betroffen (z. B. eine Hibernate-Sitzung, die nach einer Ausnahme möglicherweise inaktiv ist). - Eine vollständige (Trans-) Aktion wird nicht nur zur Hälfte annulliert. Dies bedeutet, dass solch eine Ausnahmebehandlung nur an speziellen Orten auftreten kann, normalerweise direkt unter der Benutzeroberfläche. Beispiel: Der Benutzer drückt eine Schaltfläche, die einen Kunden laden soll, ändert seine Adresse und speichert sie erneut. Wenn einer dieser Fehler auftritt, können Sie die Ausnahme abfangen und verhindern, dass alle verbleibenden Schritte ausgeführt werden. Werfen Sie das Kundenobjekt weg und zeigen Sie eine Meldung an, die besagt: Tut mir leid, hat nicht funktioniert.
Wenn Sie jedoch die Ausnahme abfangen (z. B. beim Ändern der Adresse) und dann weiterarbeiten, werden die Benutzerdinge, die ein Kundenobjekt hat, unter seiner Kontrolle geändert, während er in Wirklichkeit ein Kundenobjekt mit einer zerstörten Adresse steuert. Keine gute Situation.
Die Regel lautet also: Definieren Sie den Layer, auf dem Sie Transaktionen steuern. Dies sollte der Layer sein, der Exceptions abfängt. Könnte SwingActions sein, könnte Threads sein, könnte öffentliches void main sein, könnte innerhalb einer Schleife sein wie in deinem Beispiel.
Ich bin schon früher erwischt worden, indem ich eine allgemeine "Ausnahme" statt des individuellen Typs eingefangen habe. Es bedeutet, dass Sie einige Informationen darüber verlieren, was tatsächlich schief gelaufen ist. Normalerweise möchten Sie jeden Typ von Ausnahme anders behandeln.
Die Ausnahmebehandlung auf oberster Ebene sollte nur dazu verwendet werden, den Fehler zu protokollieren und Ihre App neu zu starten. Wenn Sie nicht wissen, welche Art von Ausnahme den Fehler verursacht, könnte Ihr Programm in einem ungültigen Zustand sein, Sie könnten mir nicht genügend Speicher oder etwas anderes.
Es hängt davon ab, wo Sie stehen. Wenn Sie auf dem niedrigen Niveau sind, können Sie die meisten Ausnahmen vorhersehen und Sie sollten explizit nur diese fangen und den Rest aufblasen lassen. Zu dem Zeitpunkt, an dem Sie die GUI oder die Logik auf oberster Ebene erreichen, wären alle bekannten Ausnahmen abgefangen worden und Sie hätten nur noch unerwartete Ausnahmen. Jetzt haben Sie 2 Möglichkeiten:
ON ERROR RESUME NEXT
, anyone?) In beiden Fällen protokollieren Sie die Ausnahme und stellen sicher, dass Sie die Protokolle regelmäßig überprüfen.
Am Ende ist es eine "Alles hängt" -Situation. Was ist schlimmer? Mit schlechten Daten fortfahren oder unerwartet beenden? Ist es ein kritisches System? Wird die Ausgabe von einem anderen System verwendet? usw., usw.
Wie auch immer, alle kritischen Ausnahmen sollten in tieferen Ebenen der Anwendung gefunden worden sein.
Wenn Sie dank der Protokolle eine unbehandelte Ausnahme finden, korrigieren Sie den Code, testen und erneut bereitstellen.
Ein Hauptproblem bei den Ausnahmehierarchien in vielen Sprachen besteht darin, dass Ausnahmen nach Typ oder nach Klasse und nicht nach Schweregrad organisiert sind. Sehr oft, wenn eine Ausnahme ausgelöst wird, liegt es daran, dass eine Methode bestimmt wurde, bevor sie etwas getan hat, dass ihre Voraussetzungen nicht erfüllt waren. Der Aufrufer der ersten Routine, die die Ausnahme auslöst, hat zu diesem Zeitpunkt möglicherweise nichts Wichtiges getan. In vielen Fällen werden alle Aktionen, die der Aufrufer vor dem Aufruf der Routine ausgeführt hat, nur auf temporäre Variablen angewendet, die sich verdampfen, wenn die Exception aufsteigt. In der überwiegenden Mehrzahl der Fälle hat daher der Versuch, das Dokument zu laden, keine Auswirkungen auf den Zustand von noch vorhandenem Material, wenn etwas wie eine LoadDocument () - Routine eine Ausnahme auslöst. Wenn ein fehlgeschlagener Versuch, ein Dokument zu laden, keine Nebenwirkung hatte und die Anwendung bereit ist, mit der Tatsache umzugehen, dass das Dokument nicht geladen ist, gibt es keinen Grund, warum die Anwendung nicht fortgesetzt werden sollte. Die Schwierigkeit besteht darin zu wissen, ob es irgendwelche Nebenwirkungen gegeben hat.
Eine Funktion, die ich in vorhandenen Systemen zur Behandlung von Ausnahmebedingungen nicht verwenden würde, ist eine virtuelle IsResolved-Eigenschaft in Exception. Nach der Behandlung eines "catch" -Blocks überprüft das System, ob IsResolved false zurückgibt. Wenn es nicht null ist und true zurückgibt, wird eine UnresolvedExceptionException aufgerufen, mit der vorherigen Ausnahme als InnerException (UnresolvedExceptionException.IsResolved würde die IsResolved-Methode seiner InnerException aufrufen).
Die meisten IsResolved-Implementierungen würden testen, ob eine Liste von Delegaten für Funktionen, die Boolean zurückgeben, leer war. Wenn nicht, true zurück, wenn eine der aufgelisteten Delegaten True zurückgibt. Wenn eine Ausnahme impliziert, dass ein nicht vorhandenes Objekt beschädigt wurde, kann die IsResolved-Methode das Disposed-Flag dieses Objekts zurückgeben. Wenn das Objekt entsorgt wurde, wäre seine Beschädigung nicht länger relevant. Wenn das Objekt in einen Using-Block eingeschlossen wird, würde ein solches Verhalten dazu führen, dass die Ausnahme bis zu dem Punkt perkoliert, an dem das Objekt entfernt wurde, aber nicht weiter.
Wirklich fiese Ausnahmen wie OutOfMemoryException konnten ihre IsResolved-Methode nur durch einen expliziten Aufruf von OutOfMemoryException.AcknowledgeOutOfMemoryCondition glücklich machen. Bei vielen anderen Routinen, die Exceptions auslösen, würde die IsResolved-Methode sofort true zurückgeben (zB Dictionary.GetValue löst aus, weil ein Schlüssel nicht gefunden wurde, dann würde die Exception, sobald das Exception-Zeichen ausgelöst wurde, aufgelöst) .
Leider bin ich nicht sicher, wie gut solch ein Schema zu einer existierenden Plattform wie .net hinzugefügt werden könnte. Dennoch, wenn jemand in Zukunft ein ähnliches System entwickelt, scheint es ein nützliches Feature zu sein.
Tags und Links exception-handling architecture