Verwenden einer Try-finally-Validierung (ohne Fang) im Vergleich zur Enum-State-Validierung

9

Ich habe den Rat zu dieser Frage gelesen, wie eine Ausnahme so nahe wie möglich behandelt werden soll.

Mein Dilemma bei der Best Practice ist, ob man einen Versuch / Fang / Endlich verwenden sollte, um eine Aufzählung zurückzugeben (oder ein Int, das einen Wert darstellt, 0 für Fehler, 1 für OK, 2 für Warnung usw., je nach Fall), damit Eine Antwort ist immer in Ordnung, oder sollte man die Ausnahme durchlaufen lassen, damit der aufrufende Teil sich darum kümmert?

Soweit ich das beurteilen kann, kann dies je nach Fall unterschiedlich sein, sodass der ursprüngliche Rat seltsam erscheint.

Zum Beispiel möchten Sie bei einem Webdienst natürlich immer einen Status zurückgeben, sodass jede Ausnahme sofort behandelt werden muss. Nehmen wir jedoch an, Sie möchten in einer Funktion, die einige Daten über http veröffentlicht / abruft, die Ausnahme (zum Beispiel im Fall von 404), um nur zu dem zu gelangen, der es ausgelöst hat. Wenn Sie dies nicht tun, müssten Sie eine Möglichkeit erstellen, um den aufrufenden Teil über die Qualität des Ergebnisses (Fehler: 404) sowie über das Ergebnis selbst zu informieren.

Obwohl es möglich ist, die 404-Ausnahme innerhalb der Hilfsfunktion abzufangen, die die Daten abruft / veröffentlicht, sollten Sie dies tun? Ist es nur ich, der eine Smallint verwendet, um Zustände im Programm zu kennzeichnen (und sie natürlich angemessen zu dokumentieren) und diese Informationen dann für Zwecke der Überprüfung der geistigen Gesundheit (alles in Ordnung / Fehlerbehandlung) außerhalb zu verwenden?

Update: Ich hatte eine schwerwiegende / nicht schwerwiegende Ausnahme für die Hauptklassifizierung erwartet, wollte diese jedoch nicht einbeziehen, um die Antworten nicht zu beeinträchtigen. Lassen Sie mich klarstellen, worum es bei der Frage geht: Behandeln der ausgelösten Ausnahmen, nicht Auslösen von Ausnahmen. Was der gewünschte Effekt ist: Erkennen Sie einen Fehler und versuchen Sie, ihn zu beheben. Wenn eine Wiederherstellung nicht möglich ist, geben Sie das aussagekräftigste Feedback.

Beim Beispiel http get / post lautet die Frage erneut: Sollten Sie ein neues Objekt bereitstellen, das beschreibt, was mit dem ursprünglichen Anrufer passiert ist? Wenn sich dieser Helfer in einer Bibliothek befindet, die Sie verwenden, würden Sie erwarten, dass er Ihnen einen Statuscode für den Vorgang liefert, oder würden Sie ihn in einen Try-Catch-Block aufnehmen? Wenn Sie es entwerfen , würden Sie einen Statuscode angeben oder eine Ausnahme auslösen und die obere Ebene ihn stattdessen in einen Statuscode / eine Statusnachricht übersetzen lassen?

Synopsis: Wie wählen Sie aus, ob ein Code anstelle einer Ausnahme einen Statuscode zusammen mit den daraus resultierenden Ergebnissen zurückgibt?

Mihalis Bagos
quelle
1
Hier geht es nicht um den Fehler, der die Form der verwendeten Fehlerbehandlung ändert. Im Fall 404 würden Sie es passieren lassen, weil Sie nicht in der Lage sind, damit umzugehen.
Steinmetz
"Ein Int, das einen Wert darstellt, 0 für Fehler, 1 für OK, 2 für Warnung usw." Bitte sagen Sie, dies war ein Beispiel ohne Manschette! Die Verwendung von 0 für OK ist definitiv der Standard ...
Anaximander

Antworten:

15

Ausnahmen sollten für außergewöhnliche Bedingungen verwendet werden. Das Auslösen einer Ausnahme bedeutet im Grunde die Aussage: "Ich kann diese Bedingung hier nicht behandeln. Kann jemand weiter oben im Aufrufstapel dies für mich abfangen und damit umgehen?"

Die Rückgabe eines Wertes kann vorzuziehen sein, wenn klar ist, dass der Anrufer diesen Wert nimmt und etwas Sinnvolles daraus macht. Dies gilt insbesondere dann, wenn das Auslösen einer Ausnahme Auswirkungen auf die Leistung hat, dh in einer engen Schleife auftreten kann. Das Auslösen einer Ausnahme dauert viel länger als das Zurückgeben eines Werts (um mindestens zwei Größenordnungen).

Ausnahmen sollten niemals zur Implementierung der Programmlogik verwendet werden. Mit anderen Worten, werfen Sie keine Ausnahme, um etwas zu erledigen. eine Ausnahme auslösen, um anzugeben, dass dies nicht möglich ist.

Robert Harvey
quelle
Vielen Dank für die Antwort, sie ist am informativsten, aber mein Fokus liegt auf der Ausnahmebehandlung und nicht auf dem Auslösen von Ausnahmen. Sollten Sie die 404-Ausnahme abfangen, sobald Sie sie erhalten, oder sollten Sie sie höher im Stapel liegen lassen?
Mihalis Bagos
Ich sehe Ihre Bearbeitung, aber meine Antwort ändert sich nicht. Konvertieren Sie die Ausnahme in einen Fehlercode, wenn dies für den Aufrufer von Bedeutung ist. Ist dies nicht der Fall, behandeln Sie die Ausnahme. lass es den Stapel hochgehen; oder fangen Sie es, machen Sie etwas damit (wie schreiben Sie es in ein Protokoll oder etwas anderes) und werfen Sie es erneut.
Robert Harvey
1
+1: für eine vernünftige und ausgewogene Erklärung. Für Ausnahmefälle sollte eine Ausnahme verwendet werden. Andernfalls sollte eine Funktion oder Methode einen aussagekräftigen Wert (Aufzählung oder Optionstyp) zurückgeben und der Aufrufer sollte ihn ordnungsgemäß behandeln. Vielleicht könnte man eine dritte Alternative erwähnen, die in der funktionalen Programmierung beliebt ist, nämlich Fortsetzungen.
Giorgio
4

Einige gute Ratschläge, die ich einmal gelesen habe, waren: Ausnahmen auslösen, wenn Sie angesichts des Status der Daten, mit denen Sie sich befassen, nicht weiterkommen können. Wenn Sie jedoch eine Methode haben, die eine Ausnahme auslösen kann, geben Sie nach Möglichkeit auch eine Methode an, um festzustellen, ob die Daten tatsächlich vorhanden sind gültig, bevor die Methode aufgerufen wird.

Beispielsweise löst System.IO.File.OpenRead () eine FileNotFoundException aus, wenn die angegebene Datei nicht vorhanden ist. Es wird jedoch auch eine .Exists () -Methode bereitgestellt, die einen booleschen Wert zurückgibt, der angibt, ob die Datei vorhanden ist, die Sie zuvor aufrufen sollten Aufruf von OpenRead (), um unerwartete Ausnahmen zu vermeiden.

Um den Teil der Frage zu beantworten, wann ich mich mit einer Ausnahme befassen soll, würde ich sagen, wo immer Sie tatsächlich etwas dagegen tun können. Wenn Ihre Methode eine Ausnahme, die von einer aufgerufenen Methode ausgelöst wird, nicht verarbeiten kann, fangen Sie sie nicht ab. Lassen Sie es die Anrufkette höher anheben, um etwas zu erreichen, das damit umgehen kann. In einigen Fällen ist dies möglicherweise nur ein Logger, der Application.UnhandledException abhört.

Trevor Pilley
quelle
Viele Leute, insbesondere Python- Programmierer, bevorzugen EAFP, dh "Es ist einfacher, um Vergebung zu bitten, als um Erlaubnis zu bitten"
Mark Booth
1
+1 für einen Kommentar zum Vermeiden von Ausnahmen wie bei .Exists ().
Codingoutloud
+1 Dies ist immer noch ein guter Rat. In Code, den ich schreibe / verwalte, ist eine Ausnahme "Außergewöhnlich". 9/10 Mal ist eine Ausnahme für einen Entwickler vorgesehen. Es heißt, hey, Sie sollten defensiv programmieren! Das andere Mal ist es etwas, mit dem wir uns nicht befassen können, und wir protokollieren es und beenden es so gut wir können. Konsistenz ist wichtig, zum Beispiel haben wir normalerweise eine echte falsche Antwort und interne Nachrichten für den Standardtarif / die Standardverarbeitung. APIs von Drittanbietern, die anscheinend Ausnahmen für alles auslösen, können beim Anruf behandelt und unter Verwendung des vereinbarten Standardverfahrens zurückgegeben werden.
Gavin Howden
3

Verwenden Sie try / catch / finally, um eine Aufzählung zurückzugeben (oder ein int, das einen Wert darstellt, 0 für Fehler, 1 für OK, 2 für Warnung usw., je nach Fall), damit eine Antwort immer in Ordnung ist.

Das ist ein schreckliches Design. Maskieren Sie keine Ausnahme, indem Sie sie in einen numerischen Code übersetzen. Lassen Sie es als eine richtige, eindeutige Ausnahme.

oder sollte man die ausnahme durchlaufen lassen, damit der aufrufende teil sich damit befasst?

Dafür gibt es Ausnahmen.

so nah wie möglich an dem Ort behandelt werden, an dem es angehoben wird

Ist überhaupt keine universelle Wahrheit. Manchmal ist es eine gute Idee. In anderen Fällen ist es nicht so hilfreich.

S.Lott
quelle
Es ist kein schreckliches Design. Microsoft implementiert es an vielen Stellen, nämlich auf dem Standardanbieter für asp.NET-Mitgliedschaft. Ansonsten kann ich nicht sehen, wie diese Antwort etwas zum Gespräch
beiträgt
@MihalisBagos: Ich kann nur vorschlagen, dass der Ansatz von Microsoft nicht von jeder Programmiersprache unterstützt wird. In Sprachen ohne Ausnahmen ist die Rückgabe eines Werts unerlässlich. C ist das bemerkenswerteste Beispiel. In Sprachen mit Ausnahmen ist die Rückgabe von "Codewerten" zur Angabe von Fehlern ein schreckliches Design. Dies führt zu (manchmal) umständlichen ifAnweisungen anstelle einer (manchmal) einfacheren Ausnahmebehandlung. Die Antwort ("Wie wählen Sie ...") ist einfach. Tu es nicht . Ich denke, dies zu sagen, trägt zum Gespräch bei. Sie können diese Antwort jedoch ignorieren.
S.Lott
Ich sage nicht, dass Ihre Meinung nicht zählt, aber ich sage, dass Ihre Meinung nicht entwickelt ist. Mit diesem Kommentar gehe ich davon aus, dass wir, wo wir Ausnahmen verwenden können, sollten, nur weil wir können? Ich sehe den Statuscode nicht als Maskierung, sondern als Kategorisierung / Organisation der Code-Flow-Fälle
Mihalis Bagos,
1
Die Ausnahme ist bereits eine Kategorisierung / Organisation. Ich sehe den Wert nicht darin, eine eindeutige Ausnahme durch einen Rückgabewert zu ersetzen, der leicht mit "normalen" oder "nicht außergewöhnlichen" Rückgabewerten verwechselt werden kann. Rückgabewerte sollten immer nicht außergewöhnlich sein. Meine Meinung ist nicht sehr entwickelt: Es ist sehr einfach. Verwandeln Sie eine Ausnahme nicht in einen numerischen Code. Es war bereits ein perfektes Datenobjekt, das alle denkbaren Anwendungsfälle erfüllt.
S.Lott
3

Ich stimme S.Lott zu . Je nach Situation kann es eine gute oder eine schlechte Idee sein, die Ausnahme so nah wie möglich an der Quelle abzufangen. Der Schlüssel zur Behandlung von Ausnahmen besteht darin, sie nur dann abzufangen, wenn Sie etwas dagegen tun können. Das Abfangen und Zurückgeben eines numerischen Werts an die aufrufende Funktion ist im Allgemeinen ein schlechtes Design. Sie möchten sie nur schweben lassen, bis Sie sich erholen können.

Ich betrachte die Ausnahmebehandlung immer als einen Schritt von meiner Anwendungslogik entfernt. Ich frage mich: Wenn diese Ausnahme ausgelöst wird, wie weit muss der Aufrufstapel gesichert werden, bevor sich meine Anwendung in einem wiederherstellbaren Zustand befindet? In vielen Fällen, wenn ich in der Anwendung nichts tun kann, um sie wiederherzustellen, kann dies bedeuten, dass ich sie erst auf der obersten Ebene abfange und nur die Ausnahme protokolliere, den Job fehlschlage und versuche, ihn sauber herunterzufahren.

Es gibt wirklich keine feste Regel, wann und wie die Ausnahmebehandlung eingerichtet werden soll, außer sie in Ruhe zu lassen, bis Sie wissen, was Sie mit ihnen tun sollen.

DwayneAllen
quelle
1

Ausnahmen sind schöne Dinge. Mit ihnen können Sie eine klare Beschreibung eines Laufzeitproblems erstellen, ohne auf unnötige Mehrdeutigkeiten zurückgreifen zu müssen. Ausnahmen können typisiert, untertypisiert und nach Typ behandelt werden. Sie können zur Verarbeitung an anderer Stelle weitergegeben werden. Wenn sie nicht verarbeitet werden können, können sie erneut angehoben werden, um auf einer höheren Ebene in Ihrer Anwendung behandelt zu werden. Sie kehren auch automatisch von Ihrer Methode zurück, ohne dass Sie eine Menge verrückter Logik aufrufen müssen, um mit verschleierten Fehlercodes umzugehen.

Sie sollten sofort nach dem Auftreten ungültiger Daten in Ihrem Code eine Ausnahme auslösen. Sie sollten Aufrufe anderer Methoden in einem try..catch..finally-Vorgang einschließen, um eventuell ausgelöste Ausnahmen zu behandeln. Wenn Sie nicht wissen, wie Sie auf eine bestimmte Ausnahme reagieren sollen, geben Sie sie erneut aus, um höheren Ebenen anzuzeigen, dass Es stimmt etwas nicht, das an anderer Stelle behandelt werden sollte.

Das Verwalten von Fehlercodes kann sehr schwierig sein. Normalerweise haben Sie viele unnötige Duplikate in Ihrem Code und / oder viele unübersichtliche Logik, um mit Fehlerzuständen umzugehen. Ein Benutzer zu sein und auf einen Fehlercode zu stoßen, ist noch schlimmer, da der Code selbst nicht aussagekräftig ist und dem Benutzer keinen Kontext für den Fehler liefert. Eine Ausnahme hingegen kann dem Benutzer etwas Nützliches mitteilen, z. B. "Sie haben vergessen, einen Wert einzugeben" oder "Sie haben einen ungültigen Wert eingegeben, hier ist der gültige Bereich, den Sie verwenden können ..." oder "Ich nicht" Wenn Sie wissen, was passiert ist, wenden Sie sich an den technischen Support und teilen Sie ihnen mit, dass ich gerade abgestürzt bin, und geben Sie ihnen die folgende Stapelverfolgung ... ".

Meine Frage an das OP lautet also, warum um alles in der Welt möchten Sie KEINE Ausnahmen für die Rückgabe von Fehlercodes verwenden?

S.Robins
quelle
0

Alles gute Antworten. Ich möchte auch hinzufügen, dass die Rückgabe eines Fehlercodes anstelle einer Ausnahme den Code des Anrufers komplizierter machen kann. Angenommen, Methode A ruft Methode B auf, ruft Methode C auf, und C stößt auf einen Fehler. Wenn C einen Fehlercode zurückgibt, muss B nun über eine Logik verfügen, um festzustellen, ob dieser Fehlercode verarbeitet werden kann. Wenn dies nicht möglich ist, muss es an A zurückgegeben werden. Wenn A den Fehler nicht behandeln kann, was tun Sie dann? Eine Ausnahme machen? In diesem Beispiel ist der Code viel sauberer, wenn C einfach eine Ausnahme auslöst, B die Ausnahme nicht abfängt, sodass er automatisch abgebrochen wird, ohne dass zusätzlicher Code erforderlich ist, und A bestimmte Arten von Ausnahmen abfangen kann, während andere den Anruf fortsetzen Stapel.

Dies erinnert an eine gute Regel zum Codieren durch:

Codezeilen sind wie goldene Kugeln. Sie möchten so wenig wie möglich verwenden, um die Arbeit zu erledigen.

Raymond Saltrelli
quelle
0

Ich habe eine Kombination beider Lösungen verwendet: Für jede Validierungsfunktion übergebe ich einen Datensatz, den ich mit dem Validierungsstatus (einem Fehlercode) fülle. Wenn am Ende der Funktion ein Validierungsfehler vorliegt, wird eine Ausnahme ausgelöst. Auf diese Weise wird keine Ausnahme für jedes Feld ausgelöst, sondern nur einmal.

Ich habe auch den Vorteil genutzt, dass das Auslösen einer Ausnahme die Ausführung stoppt, da ich nicht möchte, dass die Ausführung fortgesetzt wird, wenn Daten ungültig sind.

Beispielsweise

procedure Validate(var R:TValidationRecord);
begin
  if Field1 is not valid then
  begin
    R.Field1ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end; 
  if Field2 is not valid then
  begin
    R.Field2ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;
  if Field3 is not valid then
  begin
    R.Field3ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;

  if ErrorFlag then
    ThrowException
end;

Wenn Sie sich nur auf Boolesche Werte verlassen, sollte der Entwickler, der meine Funktion verwendet, Folgendes berücksichtigen:

if not Validate() then
  DoNotContinue();

aber er kann vergessen und nur anrufen Validate()(ich weiß, dass er nicht sollte, aber vielleicht könnte er).

Im obigen Code habe ich also die beiden Vorteile erhalten:

  1. Nur eine Ausnahme in der Validierungsfunktion.
  2. Eine Ausnahme, auch wenn sie nicht erfasst wurde, stoppt die Ausführung und wird zur Testzeit angezeigt
Bahaa
quelle
0

Hier gibt es keine Antwort - so wie es keine einzige Art von HttpException gibt.

Es ist sehr sinnvoll, dass die zugrunde liegenden HTTP-Bibliotheken eine Ausnahme auslösen, wenn sie eine 4xx- oder 5xx-Antwort erhalten. Das letzte Mal, als ich mir die HTTP-Spezifikationen angesehen habe, waren das Fehler.

Was das Auslösen dieser Ausnahme - oder das Umschließen und erneute Auslösen - betrifft, denke ich, dass dies wirklich eine Frage des Anwendungsfalls ist. Wenn Sie beispielsweise einen Wrapper schreiben, um einige Daten aus der API abzurufen und für Anwendungen verfügbar zu machen, können Sie entscheiden, dass semantisch eine Anforderung für eine nicht vorhandene Ressource, die ein HTTP 404 zurückgibt, sinnvoller ist, diese abzufangen und null zurückzugeben. Andererseits kann ein 406-Fehler (nicht akzeptabel) einen Fehler wert sein, da sich dadurch etwas geändert hat und die App abstürzen und brennen und um Hilfe schreien sollte.

Wyatt Barnett
quelle