Wann eine Ausnahme auslösen?


435

Ich habe Ausnahmen für jede Bedingung erstellt, die meine Anwendung nicht erwartet. UserNameNotValidException, PasswordNotCorrectExceptionUsw.

Mir wurde jedoch gesagt, ich sollte keine Ausnahmen für diese Bedingungen schaffen. In meiner UML sind dies Ausnahmen vom Hauptfluss. Warum sollte es also keine Ausnahme sein?

Irgendwelche Anleitungen oder Best Practices zum Erstellen von Ausnahmen?


58
Bitte wieder öffnen, dies ist eine sehr vernünftige und gültige Frage. Jede Frage beinhaltet eine gewisse Meinung, aber in diesem Fall vermute ich, dass es sich um eine „Best Practice“ handelt.
Tim Long

19
+1 zum erneuten Öffnen. Wie bei vielen anderen interessanten Themen kommt es darauf an und es ist sehr nützlich, die Kompromisse bei Entscheidungen zu analysieren. Die Tatsache, dass Menschen Meinungen mit Fakten in den Antworten verwechseln, negiert dies nicht. Das Durchsuchen des Schlamms ist eine Übung, die dem Leser überlassen werden sollte.
Aron

12
Ich stimme auch zu, dass diese Frage erneut gestellt werden sollte, da sie sich auf bewährte Verfahren bezieht. Best Practices sind übrigens immer Meinungen, die anderen helfen können.
Ajay Sharma

6
Microsoft sagt: "Geben Sie keine Fehlercodes zurück. Ausnahmen sind das primäre Mittel zum Melden von Fehlern in Frameworks." und "... Wenn ein Mitglied nicht erfolgreich das tun kann, wofür es entwickelt wurde, sollte dies als Ausführungsfehler betrachtet und eine Ausnahme ausgelöst werden." msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75

4
Dies kann eine völlig sinnvolle Ausnahme sein, es hängt nur davon ab, welche Methoden sie auslösen. Eine aufgerufene Methode IsCredentialsValid(username,password)sollte keine Ausnahme auslösen, wenn der Benutzername oder das Kennwort ungültig sind, sondern false zurückgeben. Angenommen, eine Methode, die Daten aus der Datenbank liest, könnte eine solche Ausnahme legitim auslösen, wenn die Authentifizierung fehlschlägt. Kurz gesagt: Sie sollten eine Ausnahme auslösen, wenn eine Methode die Aufgabe, die sie ausführen soll, nicht ausführen kann.
JacquesB

Antworten:


629

Meine persönliche Richtlinie lautet: Eine Ausnahme wird ausgelöst, wenn festgestellt wird, dass eine grundlegende Annahme des aktuellen Codeblocks falsch ist.

Beispiel 1: Angenommen, ich habe eine Funktion, die eine beliebige Klasse untersuchen und true zurückgeben soll, wenn diese Klasse von List <> erbt. Diese Funktion stellt die Frage: "Ist dieses Objekt ein Nachkomme von List?" Diese Funktion sollte niemals eine Ausnahme auslösen, da in ihrer Operation keine Grauzonen vorhanden sind. Jede einzelne Klasse erbt oder erbt nicht von List <>, daher lautet die Antwort immer "Ja" oder "Nein".

Beispiel 2: Angenommen, ich habe eine andere Funktion, die eine Liste <> untersucht und true zurückgibt, wenn ihre Länge mehr als 50 beträgt, und false, wenn die Länge kleiner ist. Diese Funktion stellt die Frage: "Enthält diese Liste mehr als 50 Elemente?" Bei dieser Frage wird jedoch davon ausgegangen, dass es sich bei dem angegebenen Objekt um eine Liste handelt. Wenn ich ihm NULL gebe, ist diese Annahme falsch. Wenn in diesem Fall die Funktion entweder true oder false zurückgibt , verstößt sie gegen ihre eigenen Regeln. Die Funktion kann nichts zurückgeben und behauptet, die Frage richtig beantwortet zu haben. Es kehrt also nicht zurück - es löst eine Ausnahme aus.

Dies ist vergleichbar mit dem logischen Irrtum "geladene Frage" . Jede Funktion stellt eine Frage. Wenn die eingegebene Eingabe diese Frage zu einem Irrtum macht, wird eine Ausnahme ausgelöst. Diese Linie ist mit Funktionen, die void zurückgeben, schwieriger zu zeichnen, aber die Quintessenz lautet: Wenn die Annahmen der Funktion über ihre Eingaben verletzt werden, sollte sie eine Ausnahme auslösen, anstatt normal zurückzukehren.

Die andere Seite dieser Gleichung lautet: Wenn Ihre Funktionen häufig Ausnahmen auslösen, müssen Sie wahrscheinlich ihre Annahmen verfeinern.


15
Genau! Eine Ausnahme wird ausgelöst, wenn und nur wenn Funktionsvoraussetzungen (Annahmen über Argumente) gebrochen sind !
Lightman

11
In der Linguistik wird dies manchmal als Voraussetzungsversagen bezeichnet . Das klassische Beispiel geht auf Bertrand Russell zurück: "Ist der König von Frankreich kahl" kann nicht mit Ja oder Nein beantwortet werden (bzw. "Der König von Frankreich ist kahl" ist weder wahr noch falsch), weil es eine falsche Voraussetzung enthält , nämlich dass es einen König von Frankreich gibt. Voraussetzungsfehler werden oft mit eindeutigen Beschreibungen gesehen, und das ist beim Programmieren üblich. Beispiel: "Der Kopf einer Liste" hat einen Fehler bei der Voraussetzung, wenn eine Liste leer ist, und dann ist es angebracht, eine Ausnahme auszulösen.
Mohan

Dies ist möglicherweise die beste Erklärung!
Gaurav

Vielen Dank. Damit. Viel. "Ist der König von Frankreich kahl". Ich habe das schon einmal gehört, als ich meinenongs Dschungel erforscht habe .... :) Danke. @ Mohan
ErlichBachman

285

Weil es Dinge sind, die normal passieren werden. Ausnahmen sind keine Kontrollflussmechanismen. Benutzer erhalten häufig falsche Passwörter, dies ist kein Ausnahmefall. Ausnahmen sollten eine wirklich seltene Sache sein, UserHasDiedAtKeyboardTyp Situationen.


3
Hmm, nein. Ausnahmen können als Kontrollflussmechanismen verwendet werden, wenn keine maximale Leistung erforderlich ist, was für die meisten Web-Apps gilt. Python verwendet die Ausnahme 'StopIteration', um Iteratoren zu beenden, und es funktioniert sehr gut. Die Kosten sind nichts im Vergleich zu IO usw.
Seun Osewa

9
+1 ausgezeichnete Antwort. Ich bin so frustriert von Entwicklern, die an APIs arbeiten, dass ich für jede Kleinigkeit Ausnahmen verbrauchen und auslösen muss. Sehr wenige Fälle erfordern wirklich Ausnahmen. Wenn Sie 25 verschiedene Arten von Ausnahmen definiert haben, sehen Sie sich Ihr Design noch einmal an. Möglicherweise machen Sie es falsch.
7wp

1
Sollte es eine Ausnahme geben, wenn der Benutzer eine illegale Aktion versucht, die er nicht durch Manipulation des Webseitencodes ausführen darf, z. B. um die Beiträge anderer Personen hier bei StackOverflow zu löschen?
Rajat Gupta

30
Ausnahmen sind Kontrollflussmechanismen. Du kannst sie werfen. Sie können sie fangen. Sie haben die Steuerung auf einen anderen Code verschoben. Das ist Kontrollfluss. Der einzige Grund, warum Sprachen überhaupt Ausnahmen haben, ist, dass Sie einfachen Code schreiben können, ohne zu fragen: "Ist das Ding einfach fehlgeschlagen?" nach allem was du tust. Haskell zum Beispiel hat keine Ausnahmen, da die Monaden und die Do-Notation die Fehlerprüfung für Sie automatisieren können.
Jesse

2
Ausnahmen sind mehr als Kontrollflussmechanismen. Sie geben dem (Methoden-) Kunden nützliche Informationen über außergewöhnliche Ergebnisse, die er kennen und handhaben muss. Das heißt, richtig verwendet, machen Ausnahmen APIs robuster
idelvall

67

Meine kleinen Richtlinien sind stark vom großen Buch "Code complete" beeinflusst:

  • Verwenden Sie Ausnahmen, um über Dinge zu informieren, die nicht ignoriert werden sollten.
  • Verwenden Sie keine Ausnahmen, wenn der Fehler lokal behandelt werden kann
  • Stellen Sie sicher, dass sich die Ausnahmen auf derselben Abstraktionsebene befinden wie der Rest Ihrer Routine.
  • Ausnahmen sollten für das reserviert werden, was wirklich außergewöhnlich ist .

35

Es ist KEINE Ausnahme, wenn der Benutzername ungültig oder das Passwort nicht korrekt ist. Das sind Dinge, die Sie im normalen Betriebsablauf erwarten sollten. Ausnahmen sind Dinge, die nicht zum normalen Programmbetrieb gehören und eher selten sind.

BEARBEITEN: Ich mag es nicht, Ausnahmen zu verwenden, da Sie nicht erkennen können, ob eine Methode eine Ausnahme auslöst, indem Sie nur den Aufruf betrachten. Aus diesem Grund sollten Ausnahmen nur verwendet werden, wenn Sie mit der Situation nicht angemessen umgehen können (denken Sie an "nicht genügend Speicher" oder "Computer brennt").


"Ich mag es nicht, Ausnahmen zu verwenden, weil Sie nicht erkennen können, ob eine Methode eine Ausnahme auslöst, indem Sie sich nur den Aufruf ansehen." Aus diesem Grund gibt es geprüfte Ausnahmen für Sprachen, die sie unterstützen.
Newtopian

1
Überprüfte Ausnahmen haben ihre eigenen Probleme. Ich würde immer noch lieber Ausnahmen von "außergewöhnlichen Umständen" verwenden, nicht für Dinge, die Teil des normalen Workflows sind.
EricSchaefer

2
Als Antwort auf Ihre Bearbeitung. Ich habe immer in meine XML-Dokumente am Ende des Zusammenfassungsabschnitts die Ausnahmen eingefügt, die die Funktion auslöst, damit ich diese Informationen in Intellisense sehen kann.
Matthew Vines

1
Sollte es eine Ausnahme geben, wenn der Benutzer eine illegale Aktion versucht, die er nicht durch Manipulation des Webseitencodes ausführen darf, z. B. um die Beiträge anderer Personen hier bei StackOverflow zu löschen?
Rajat Gupta

1
Es hört sich so an, als würden Sie über die Behandlung von Fehlern (Speicher, Computer in Flammen) und die Behandlung von Code-Ausnahmen (fehlender Datensatz, ungültiger Eingabetyp usw.) sprechen. Ich denke, es gibt einen deutlichen Unterschied zwischen den beiden.
Chuck Burgess

28

Eine Faustregel ist, Ausnahmen für etwas zu verwenden, das Sie normalerweise nicht vorhersagen können. Beispiele sind Datenbankkonnektivität, fehlende Datei auf der Festplatte usw. Für Szenarien, die Sie vorhersagen können, z. B. Benutzer, die versuchen, sich mit einem falschen Kennwort anzumelden, sollten Sie Funktionen verwenden, die Boolesche Werte zurückgeben und wissen, wie sie mit der Situation ordnungsgemäß umgehen. Sie möchten die Ausführung nicht abrupt beenden, indem Sie eine Ausnahme auslösen, nur weil jemand sein Kennwort falsch eingegeben hat.


6
Sie müssen die Programmausführung für eine Ausnahme nicht stoppen ... Wenn Sie die Ausnahme auslösen, fängt der Aufrufer dann die Ausnahme ab und sollte sie, wenn möglich, protokollieren und fehlerhaft behandeln und fortfahren. Es ist eine „schlechte Form“, immer wieder Ausnahmen in den Aufrufstapel zu werfen - fangen Sie ihn dort, wo er auftritt, und behandeln Sie ihn dort
Krakkos,

2
aber warum sie überhaupt werfen, wenn Sie direkt damit umgehen können. Wenn das Passwort falsch ist oder irgendetwas falsch ist, lasse ich einfach ein if ein falsches zurückgeben und gebe einen Fehler aus
My1

" fehlende Datei auf der Festplatte " Die meisten Sprachframeworks, z. B. .NET Framework, bieten APIs, mit denen auch die Existenz von Dateien überprüft werden kann. Warum nicht verwenden, bevor Sie direkt auf die Datei zugreifen?
user1451111

23

Andere schlagen vor, dass Ausnahmen nicht verwendet werden sollten, da die fehlerhafte Anmeldung in einem normalen Ablauf zu erwarten ist, wenn der Benutzer einen falschen Typ eingibt. Ich bin anderer Meinung und verstehe die Begründung nicht. Vergleichen Sie es mit dem Öffnen einer Datei. Wenn die Datei nicht vorhanden ist oder aus irgendeinem Grund nicht verfügbar ist, wird vom Framework eine Ausnahme ausgelöst. Die Verwendung der obigen Logik war ein Fehler von Microsoft. Sie sollten einen Fehlercode zurückgegeben haben. Gleiches gilt für das Parsen, Webanfragen usw. usw.

Ich betrachte ein schlechtes Login nicht als Teil eines normalen Ablaufs, es ist außergewöhnlich. Normalerweise gibt der Benutzer das richtige Passwort ein und die Datei existiert. Die Ausnahmefälle sind außergewöhnlich und es ist vollkommen in Ordnung, Ausnahmen für diese zu verwenden. Die Komplikation Ihres Codes durch die Weitergabe von Rückgabewerten über n Ebenen im Stapel ist eine Energieverschwendung und führt zu unordentlichem Code. Machen Sie das Einfachste, was möglicherweise funktionieren könnte. Optimieren Sie nicht vorzeitig mithilfe von Fehlercodes, außergewöhnliche Dinge kommen per Definition selten vor und Ausnahmen kosten nichts, es sei denn, Sie werfen sie aus.


Außer Sie können überprüfen, ob die Datei vorhanden ist, bevor Sie open aufrufen (natürlich abhängig von Ihrem Framework). Diese Funktion ist also vorhanden. Wenn die Datei zwischen der Prüfung und dem Versuch, sie zu öffnen, verschwindet, ist dies eine Ausnahme.
Blowdart

7
Die vorhandene Datei bedeutet nicht, dass der Benutzer beispielsweise in die Datei schreiben darf. Das Überprüfen auf jedes mögliche Problem ist sehr mühsam und fehleranfällig. + Sie duplizieren den Code (DRY).
Björn Reppen

Ein Punkt mit dem ungültigen Passwort Ausnahme ist, dass eine Langsamkeit im Vergleich zur Rückkehrcode-Lösung für den menschlichen Benutzer, der ein Passwort eingibt, nicht wahrnehmbar ist.
Papierpferd

7
"Die Komplikation Ihres Codes durch die Weitergabe von Rückgabewerten über n Ebenen im Stapel ist eine Energieverschwendung und führt zu unordentlichem Code." Das ist für mich ein sehr starker Grund, Ausnahmen zu verwenden. Guter Code besteht normalerweise aus kleinen Funktionen. Sie möchten diesen Fehlercode nicht immer wieder von einer kleinen Funktion an eine andere weitergeben.
Beluchin

Ich denke, die Verwirrung resultiert aus der Annahme, dass ein vorhersehbares Ergebnis einer loginMethode vom Typ möglicherweise darin besteht, dass ein Kennwort falsch ist, dass es tatsächlich verwendet wird, um dies zu bestimmen, und dass in diesem Fall keine Ausnahme gewünscht wird. In einem file openTypenszenario, in dem ein bestimmtes Ergebnis gewünscht wird. Wenn das System das Ergebnis aufgrund falscher Eingabeparameter oder eines externen Faktors nicht liefern kann, ist dies eine vollkommen logische Verwendung einer Ausnahme.
theMayer

17

Ausnahmen sind ein etwas kostspieliger Effekt. Wenn Sie beispielsweise einen Benutzer haben, der ein ungültiges Kennwort angibt, ist es normalerweise besser, ein Fehlerflag oder einen anderen Indikator für die Ungültigkeit zurückzugeben.

Dies liegt an der Art und Weise, wie Ausnahmen behandelt werden, echte fehlerhafte Eingaben und eindeutige kritische Stoppelemente sollten Ausnahmen sein, aber keine fehlgeschlagenen Anmeldeinformationen.


14

Ich denke, Sie sollten nur dann eine Ausnahme auslösen, wenn Sie nichts tun können, um aus Ihrem aktuellen Zustand herauszukommen. Zum Beispiel, wenn Sie Speicher zuweisen und keine zuzuweisen ist. In den von Ihnen genannten Fällen können Sie sich eindeutig von diesen Zuständen erholen und einen entsprechenden Fehlercode an Ihren Anrufer zurücksenden.


Sie werden viele Ratschläge sehen, auch in Antworten auf diese Frage, dass Sie Ausnahmen nur unter "außergewöhnlichen" Umständen auslösen sollten. Das erscheint oberflächlich vernünftig, ist aber ein fehlerhafter Rat, da es eine Frage ("wann sollte ich eine Ausnahme auslösen") durch eine andere subjektive Frage ("was ist außergewöhnlich") ersetzt. Befolgen Sie stattdessen den Rat von Herb Sutter (für C ++, verfügbar im Artikel Wann und wie Ausnahmen von Dr. Dobbs verwendet werden , sowie in seinem Buch mit Andrei Alexandrescu, C ++ Coding Standards ): Wenn und nur wenn eine Ausnahme auslösen

  • eine Voraussetzung ist nicht erfüllt (was normalerweise eine der folgenden Möglichkeiten unmöglich macht) oder
  • die Alternative würde eine Nachbedingung nicht erfüllen oder
  • Die Alternative würde es nicht schaffen, eine Invariante aufrechtzuerhalten.

Warum ist das besser? Ersetzt es die Frage nicht durch mehrere Fragen zu Vorbedingungen, Nachbedingungen und Invarianten? Dies ist aus mehreren miteinander verbundenen Gründen besser.

  • Voraussetzungen, Nachbedingungen und Invarianten sind Designmerkmale unseres Programms (seiner internen API), während die Entscheidung dazuthrow ein Implementierungsdetail ist. Es zwingt uns zu bedenken, dass wir das Design und seine Implementierung separat betrachten müssen, und unsere Aufgabe bei der Implementierung einer Methode ist es, etwas zu produzieren, das die Designbeschränkungen erfüllt.
  • Es zwingt uns, in Vorbedingungen, Nachbedingungen und Invarianten zu denken, die die einzigen sind Annahmen, die Aufrufer unserer Methode treffen sollten und die präzise ausgedrückt werden, um eine lose Kopplung zwischen den Komponenten unseres Programms zu ermöglichen.
  • Diese lose Kopplung ermöglicht es uns dann, die Implementierung bei Bedarf umzugestalten.
  • Die Nachbedingungen und Invarianten sind überprüfbar; Dies führt zu Code, der leicht einem Unit-Test unterzogen werden kann, da die Nachbedingungen Prädikate sind, die unser Unit-Test-Code überprüfen (bestätigen) kann.
  • Das Denken in Nachbedingungen führt natürlich zu einem Design, das als Nachbedingung erfolgreich ist. Dies ist der natürliche Stil für die Verwendung von Ausnahmen. Der normale ("glückliche") Ausführungspfad Ihres Programms ist linear angelegt, wobei der gesamte Fehlerbehandlungscode in die catchKlauseln verschoben wird .

10

Ich würde sagen, es gibt keine festen Regeln für die Verwendung von Ausnahmen. Es gibt jedoch gute Gründe, sie zu verwenden oder nicht zu verwenden:

Gründe für die Verwendung von Ausnahmen:

  • Der Code-Fluss für den allgemeinen Fall ist klarer
  • Kann komplexe Fehlerinformationen als Objekt zurückgeben (obwohl dies auch mit dem als Referenz übergebenen Fehler "out" -Parameter erreicht werden kann)
  • Sprachen bieten im Allgemeinen eine Möglichkeit zum Verwalten einer ordentlichen Bereinigung im Falle der Ausnahme (try / finally in Java unter Verwendung von C #, RAII in C ++)
  • Wenn keine Ausnahme ausgelöst wird, kann die Ausführung manchmal schneller sein als das Überprüfen von Rückkehrcodes
  • In Java müssen geprüfte Ausnahmen deklariert oder abgefangen werden (obwohl dies ein Grund dafür sein kann).

Gründe, Ausnahmen nicht zu verwenden:

  • Manchmal ist es übertrieben, wenn die Fehlerbehandlung einfach ist
  • Wenn Ausnahmen nicht dokumentiert oder deklariert sind, können sie durch Aufrufen von Code nicht erfasst werden. Dies kann schlimmer sein, als wenn der aufrufende Code nur einen Rückkehrcode ignoriert (Anwendungs-Exit vs. stiller Fehler - was schlimmer ist, kann vom Szenario abhängen).
  • In C ++ muss Code, der Ausnahmen verwendet, ausnahmesicher sein (auch wenn Sie sie nicht werfen oder abfangen, sondern indirekt eine Wurffunktion aufrufen).
  • In C ++ ist es schwer zu sagen, wann eine Funktion ausgelöst werden könnte. Daher müssen Sie in Bezug auf die Ausnahmesicherheit paranoid sein, wenn Sie sie verwenden
  • Das Auslösen und Abfangen von Ausnahmen ist im Allgemeinen erheblich teurer als das Überprüfen eines Rückgabeflags

Im Allgemeinen würde ich eher Ausnahmen in Java als in C ++ oder C # verwenden, da ich der Meinung bin, dass eine deklarierte oder nicht deklarierte Ausnahme grundsätzlich Teil der formalen Schnittstelle einer Funktion ist, da sich Ihre Ausnahmegarantie möglicherweise ändert Anrufcode unterbrechen. Der größte Vorteil der Verwendung in Java IMO besteht darin, dass Sie wissen, dass Ihr Anrufer die Ausnahme behandeln MUSS, und dies verbessert die Wahrscheinlichkeit eines korrekten Verhaltens.

Aus diesem Grund würde ich in jeder Sprache immer alle Ausnahmen in einer Code- oder API-Schicht von einer gemeinsamen Klasse ableiten, sodass der aufrufende Code immer garantieren kann, dass alle Ausnahmen abgefangen werden. Außerdem würde ich es für schlecht halten, beim Schreiben einer API oder Bibliothek implementierungsspezifische Ausnahmeklassen auszulösen (dh Ausnahmen von niedrigeren Ebenen umbrechen, damit die Ausnahme, die Ihr Aufrufer empfängt, im Kontext Ihrer Schnittstelle verständlich ist).

Beachten Sie, dass Java zwischen allgemeinen und Laufzeitausnahmen unterscheidet, da letztere nicht deklariert werden müssen. Ich würde Runtime-Ausnahmeklassen nur verwenden, wenn Sie wissen, dass der Fehler auf einen Fehler im Programm zurückzuführen ist.


5

Ausnahmeklassen sind wie "normale" Klassen. Sie erstellen eine neue Klasse, wenn es sich um einen anderen Objekttyp mit unterschiedlichen Feldern und Operationen handelt.

Als Faustregel sollten Sie versuchen, ein Gleichgewicht zwischen der Anzahl der Ausnahmen und der Granularität der Ausnahmen herzustellen. Wenn Ihre Methode mehr als 4-5 verschiedene Ausnahmen auslöst, können Sie wahrscheinlich einige davon zu "allgemeineren" Ausnahmen zusammenführen (z. B. in Ihrem Fall "AuthenticationFailedException") und mithilfe der Ausnahmemeldung detailliert beschreiben, was schief gelaufen ist. Sofern Ihr Code nicht alle unterschiedlich behandelt, müssen Sie nicht viele Ausnahmeklassen erstellen. Wenn dies der Fall ist, sollten Sie möglicherweise nur eine Aufzählung mit dem aufgetretenen Fehler zurückgeben. Auf diese Weise ist es ein bisschen sauberer.


5

Wenn es sich um Code handelt, der in einer Schleife ausgeführt wird und wahrscheinlich immer wieder eine Ausnahme verursacht, ist das Auslösen von Ausnahmen keine gute Sache, da sie für große N ziemlich langsam sind. Es ist jedoch nichts Falsches daran, benutzerdefinierte Ausnahmen auszulösen, wenn die Leistung nicht stimmt ein Problem. Stellen Sie einfach sicher, dass Sie eine Basisausnahme haben, die alle erben, BaseException oder ähnliches. BaseException erbt System.Exception, aber alle Ihre Ausnahmen erben BaseException. Sie können sogar einen Baum von Ausnahmetypen haben, um ähnliche Typen zu gruppieren, aber dies kann ein Overkill sein oder auch nicht.

Die kurze Antwort lautet also: Wenn dies keine signifikante Leistungsminderung verursacht (was nicht der Fall sein sollte, es sei denn, Sie werfen viele Ausnahmen aus), fahren Sie fort.


Ihr Kommentar zu den Ausnahmen in einer Schleife hat mir sehr gut gefallen und ich habe versucht, ihn selbst auszuprobieren. Ich habe ein Beispielprogramm geschrieben, das eine Schleifenzeit int.MaxValueausführt und darin eine Ausnahme "durch Null teilen" erzeugt. Die IF / ELSE-Version, in der ich vor der Divisionsoperation überprüft habe, ob die Dividende nicht Null ist , wurde in 6082 ms und 15407722 Ticks abgeschlossen, während die TRY / CATCH-Version, in der ich die Ausnahme generiert und die Ausnahme abgefangen habe, in 28174385 abgeschlossen wurde ms und 71371326155 Ticks: satte 4632-mal mehr als die if / else-Version.
user1451111

3

Ich bin mit Japollock weit oben einverstanden - werfen Sie eine Annahme, wenn Sie sich über das Ergebnis einer Operation nicht sicher sind. Aufrufe von APIs, Zugriff auf Dateisysteme, Datenbankaufrufe usw. Immer wenn Sie die "Grenzen" Ihrer Programmiersprachen überschreiten.

Ich möchte hinzufügen, zögern Sie nicht, eine Standardausnahme auszulösen. Wenn Sie nichts "anderes" tun (ignorieren, per E-Mail versenden, protokollieren, das Twitter-Walbild-Ding zeigen usw.), sollten Sie sich nicht mit benutzerdefinierten Ausnahmen befassen.


3

Die Faustregel für das Auslösen von Ausnahmen ist ziemlich einfach. Sie tun dies, wenn Ihr Code in den Status UNRECOVERABLE INVALID eingetreten ist. Wenn Daten kompromittiert sind oder Sie die bis zu diesem Zeitpunkt aufgetretene Verarbeitung nicht zurücksetzen können, müssen Sie sie beenden. Was können Sie sonst noch tun? Ihre Verarbeitungslogik wird irgendwann an anderer Stelle fehlschlagen. Wenn Sie sich irgendwie erholen können, dann tun Sie das und werfen Sie keine Ausnahme.

In Ihrem speziellen Fall sollten Sie den Vorgang beenden, indem Sie eine Ausnahme auslösen, um zu benachrichtigen, dass etwas Schlimmes passiert ist, und weiteren Schaden zu verhindern, wenn Sie gezwungen waren, etwas Dummes zu tun, z. B. Geld abzuheben und erst dann Benutzer / Passwort zu überprüfen.


2

Im Allgemeinen möchten Sie eine Ausnahme für alles auslösen, was in Ihrer Anwendung passieren kann, nämlich "Außergewöhnlich".

In Ihrem Beispiel sehen beide Ausnahmen so aus, als würden Sie sie über eine Kennwort- / Benutzernamenüberprüfung aufrufen. In diesem Fall kann argumentiert werden, dass es nicht wirklich außergewöhnlich ist, dass jemand einen Benutzernamen / ein Passwort falsch eingibt.

Sie sind "Ausnahmen" vom Hauptfluss Ihrer UML, aber mehr "Zweige" in der Verarbeitung.

Wenn Sie versuchen würden, auf Ihre passwd-Datei oder Datenbank zuzugreifen, dies jedoch nicht möglich wäre, wäre dies ein Ausnahmefall und würde das Auslösen einer Ausnahme rechtfertigen.


" Wenn Sie versuchen würden, auf Ihre passwd-Datei oder -Datenbank zuzugreifen, dies nicht möglich wäre, wäre dies ein Ausnahmefall und würde das Auslösen einer Ausnahme rechtfertigen. " Die meisten Sprachframeworks, z. B. .NET Framework, bieten APIs, mit denen auch die Existenz von Dateien überprüft werden kann. Warum nicht verwenden, bevor Sie direkt auf die Datei zugreifen?
user1451111

2

Erstens, wenn die Benutzer Ihrer API nicht an bestimmten, feinkörnigen Fehlern interessiert sind, sind bestimmte Ausnahmen für sie nicht von Wert.

Da es oft nicht möglich ist zu wissen, was für Ihre Benutzer nützlich sein kann, besteht ein besserer Ansatz darin, die spezifischen Ausnahmen zu haben, aber sicherzustellen, dass sie von einer gemeinsamen Klasse erben (z. B. std :: exception oder deren Ableitungen in C ++). Auf diese Weise kann Ihr Client bestimmte Ausnahmen abfangen, wenn er dies wünscht, oder die allgemeinere Ausnahme, wenn es ihn nicht interessiert.


2

Ausnahmen sind für Ereignisse gedacht, bei denen es sich um abnormale Verhaltensweisen, Fehler, Ausfälle usw. handelt. Funktionsverhalten, Benutzerfehler usw. sollten stattdessen von der Programmlogik behandelt werden. Da ein fehlerhaftes Konto oder Kennwort ein erwarteter Bestandteil des Logikflusses in einer Anmelderoutine ist, sollte es in der Lage sein, diese Situationen ausnahmslos zu behandeln.


2

Ich habe philosophische Probleme mit der Verwendung von Ausnahmen. Grundsätzlich erwarten Sie, dass ein bestimmtes Szenario auftritt, aber anstatt es explizit zu behandeln, verschieben Sie das Problem, um es "anderswo" zu behandeln. Und wo das "anderswo" ist, kann jeder erraten.


2

Ich würde sagen, dass im Allgemeinen jeder Fundamentalismus zur Hölle führt.

Sie würden sicherlich nicht mit einem ausnahmegesteuerten Ablauf enden wollen, aber es ist auch eine schlechte Idee, Ausnahmen insgesamt zu vermeiden. Sie müssen ein Gleichgewicht zwischen beiden Ansätzen finden. Was ich nicht tun würde, ist, für jede Ausnahmesituation einen Ausnahmetyp zu erstellen. Das ist nicht produktiv.

Im Allgemeinen bevorzuge ich die Erstellung von zwei grundlegenden Arten von Ausnahmen, die im gesamten System verwendet werden: LogicalException und TechnicalException . Diese können bei Bedarf durch Subtypen weiter unterschieden werden, sind jedoch im Allgemeinen nicht erforderlich.

Die technische Ausnahme bezeichnet die wirklich unerwartete Ausnahme, dass der Datenbankserver ausgefallen ist, die Verbindung zum Webdienst die IOException ausgelöst hat und so weiter.

Andererseits werden die logischen Ausnahmen verwendet, um die weniger schwerwiegende Fehlersituation auf die oberen Schichten zu übertragen (im Allgemeinen ein Validierungsergebnis).

Bitte beachten Sie, dass selbst die logische Ausnahme nicht regelmäßig zur Steuerung des Programmablaufs verwendet werden soll, sondern um die Situation hervorzuheben, in der der Ablauf tatsächlich enden sollte. Bei Verwendung in Java sind beide Ausnahmetypen RuntimeException Unterklassen, und die Fehlerbehandlung ist stark aspektorientiert.

Im Anmeldebeispiel ist es daher möglicherweise ratsam, AuthenticationException zu erstellen und die konkreten Situationen durch Enum-Werte wie UsernameNotExisting , PasswordMismatch usw. zu unterscheiden. Dann haben Sie keine große Ausnahmehierarchie und können die Catch-Blöcke auf einer wartbaren Ebene halten . Sie können auch problemlos einen allgemeinen Mechanismus zur Behandlung von Ausnahmen verwenden, da Sie die Ausnahmen kategorisiert haben und ziemlich genau wissen, was und wie an den Benutzer weitergegeben werden soll.

Unsere typische Verwendung besteht darin, die LogicalException während des Webdienstaufrufs auszulösen, wenn die Benutzereingabe ungültig war. Die Ausnahme wird dem SOAPFault-Detail zugeordnet und dann auf dem Client erneut der Ausnahme zugeordnet, was dazu führt, dass der Validierungsfehler in einem bestimmten Webseiten-Eingabefeld angezeigt wird, da die Ausnahme diesem Feld ordnungsgemäß zugeordnet ist.

Dies ist sicherlich nicht die einzige Situation: Sie müssen nicht auf den Webdienst klicken, um die Ausnahme auszulösen. Sie können dies in jeder Ausnahmesituation tun (wie in dem Fall, dass Sie schnell ausfallen müssen) - alles liegt in Ihrem Ermessen.


2

Für mich sollte eine Ausnahme ausgelöst werden, wenn eine erforderliche technische oder geschäftliche Regel fehlschlägt. Zum Beispiel, wenn eine Fahrzeugentität mit einem Array von 4 Reifen verknüpft ist ... wenn ein Reifen oder mehrere null sind ... sollte eine Ausnahme "NotEnoughTiresException" ausgelöst werden, da sie auf verschiedenen Ebenen des Systems abgefangen werden kann und eine signifikante Bedeutung hat Bedeutung durch Protokollierung. Außerdem, wenn wir nur versuchen, die Null zu kontrollieren und die Instanziierung des Autos zu verhindern. Wir werden vielleicht nie die Ursache des Problems finden, denn der Reifen sollte überhaupt nicht null sein.


1

Der Hauptgrund für das Vermeiden des Auslösens einer Ausnahme besteht darin, dass das Auslösen einer Ausnahme mit viel Aufwand verbunden ist.

Eine Sache, die der folgende Artikel besagt, ist, dass eine Ausnahme für außergewöhnliche Bedingungen und Fehler ist.

Ein falscher Benutzername ist nicht unbedingt ein Programmfehler, sondern ein Benutzerfehler ...

Hier ist ein guter Ausgangspunkt für Ausnahmen in .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx


1

Das Auslösen von Ausnahmen führt dazu, dass sich der Stapel abwickelt, was einige Auswirkungen auf die Leistung hat (zugegeben, moderne verwaltete Umgebungen haben dies verbessert). Es wäre eine schlechte Idee, in einer verschachtelten Situation immer wieder Ausnahmen zu werfen und zu fangen.

Wahrscheinlich wichtiger als das, Ausnahmen sind für außergewöhnliche Bedingungen gedacht. Sie sollten nicht für den normalen Kontrollfluss verwendet werden, da dies die Lesbarkeit Ihres Codes beeinträchtigt.


1

Ich habe drei Arten von Bedingungen, die ich fange.

  1. Eine fehlerhafte oder fehlende Eingabe sollte keine Ausnahme sein. Verwenden Sie sowohl clientseitiges js als auch serverseitigen regulären Ausdruck, um Attribute zu erkennen, festzulegen und mit Nachrichten an dieselbe Seite weiterzuleiten.

  2. Die AppException. Dies ist normalerweise eine Ausnahme, die Sie in Ihrem Code erkennen und auslösen. Mit anderen Worten, dies sind diejenigen, die Sie erwarten (die Datei existiert nicht). Protokollieren Sie es, legen Sie die Nachricht fest und leiten Sie sie zur allgemeinen Fehlerseite zurück. Diese Seite enthält normalerweise einige Informationen darüber, was passiert ist.

  3. Die unerwartete Ausnahme. Dies sind diejenigen, von denen Sie nichts wissen. Protokollieren Sie es mit Details und leiten Sie sie an eine allgemeine Fehlerseite weiter.

Hoffe das hilft


1

Die Sicherheit steht in Konflikt mit Ihrem Beispiel: Sie sollten einem Angreifer nicht mitteilen, dass ein Benutzername vorhanden ist, das Kennwort jedoch falsch ist. Das sind zusätzliche Informationen, die Sie nicht teilen müssen. Sagen Sie einfach "Der Benutzername oder das Passwort ist falsch."


1

Die einfache Antwort lautet, wann immer eine Operation unmöglich ist (entweder aufgrund einer Anwendung ODER aufgrund einer Verletzung der Geschäftslogik). Wenn eine Methode aufgerufen wird und es nicht möglich ist, das zu tun, wofür die Methode geschrieben wurde, lösen Sie eine Ausnahme aus. Ein gutes Beispiel ist, dass Konstruktoren immer ArgumentExceptions auslösen, wenn eine Instanz nicht mit den angegebenen Parametern erstellt werden kann. Ein weiteres Beispiel ist InvalidOperationException, die ausgelöst wird, wenn eine Operation aufgrund des Status eines anderen Mitglieds oder von Mitgliedern der Klasse nicht ausgeführt werden kann.

Wenn in Ihrem Fall eine Methode wie Login (Benutzername, Passwort) aufgerufen wird und der Benutzername nicht gültig ist, ist es in der Tat richtig, eine UserNameNotValidException oder eine PasswordNotCorrectException auszulösen, wenn das Passwort falsch ist. Der Benutzer kann nicht mit den angegebenen Parametern angemeldet werden (dh es ist unmöglich, da dies die Authentifizierung verletzen würde). Setzen Sie daher eine Ausnahme. Obwohl ich möglicherweise Ihre beiden Ausnahmen von ArgumentException erben lassen habe.

Wenn Sie jedoch KEINE Ausnahme auslösen möchten, weil ein Anmeldefehler sehr häufig sein kann, besteht eine Strategie darin, stattdessen eine Methode zu erstellen, die Typen zurückgibt, die unterschiedliche Fehler darstellen. Hier ist ein Beispiel:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

Den meisten Entwicklern wird beigebracht, Ausnahmen aufgrund des durch das Auslösen verursachten Overheads zu vermeiden. Es ist großartig, ressourcenbewusst zu sein, aber normalerweise nicht auf Kosten Ihres Anwendungsdesigns. Das ist wahrscheinlich der Grund, warum Ihnen gesagt wurde, dass Sie Ihre beiden Ausnahmen nicht werfen sollen. Ob Ausnahmen verwendet werden sollen oder nicht, hängt normalerweise davon ab, wie häufig die Ausnahme auftritt. Wenn es sich um ein ziemlich häufiges oder zu erwartendes Ergebnis handelt, vermeiden die meisten Entwickler Ausnahmen und erstellen stattdessen eine andere Methode, um auf einen Fehler hinzuweisen, da angeblich Ressourcen verbraucht werden.

Hier ist ein Beispiel für die Vermeidung der Verwendung von Ausnahmen in einem soeben beschriebenen Szenario mithilfe des Try () -Musters:

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

1

Meiner Meinung nach sollte die grundlegende Frage sein, ob man erwarten würde, dass der Aufrufer den normalen Programmfluss fortsetzen möchte, wenn eine Bedingung auftritt. Wenn Sie es nicht wissen, haben Sie entweder separate Methoden doSomething und trySomething, wobei die erstere einen Fehler zurückgibt und die letztere nicht, oder eine Routine, die einen Parameter akzeptiert, um anzugeben, ob eine Ausnahme ausgelöst werden soll, wenn sie fehlschlägt. Stellen Sie sich eine Klasse vor, um Befehle an ein Remote-System zu senden und Antworten zu melden. Bestimmte Befehle (z. B. Neustart) führen dazu, dass das Remote-System eine Antwort sendet, dann aber für eine bestimmte Zeit nicht reagiert. Es ist daher nützlich, einen "Ping" -Befehl senden zu können und herauszufinden, ob das Remote-System in angemessener Zeit reagiert, ohne eine Ausnahme auslösen zu müssen, wenn dies nicht der Fall ist. t (Der Anrufer würde wahrscheinlich erwarten, dass die ersten "Ping" -Versuche fehlschlagen, aber einer würde schließlich funktionieren). Auf der anderen Seite, wenn man eine Folge von Befehlen hat wie:

  exchange_command ("open tempfile");
  exchange_command ("tempfile-Daten schreiben {was auch immer}");
  exchange_command ("tempfile-Daten schreiben {was auch immer}");
  exchange_command ("tempfile-Daten schreiben {was auch immer}");
  exchange_command ("tempfile-Daten schreiben {was auch immer}");
  exchange_command ("tempfile schließen");
  exchange_command ("tempfile in realfile kopieren");

Man möchte, dass eine Operation fehlschlägt, um die gesamte Sequenz abzubrechen. Während man jede Operation überprüfen kann, um sicherzustellen, dass sie erfolgreich ist, ist es hilfreicher, wenn die Routine exchange_command () eine Ausnahme auslöst, wenn ein Befehl fehlschlägt.

Tatsächlich kann es im obigen Szenario hilfreich sein, einen Parameter zur Auswahl einer Reihe von Fehlerbehandlungsmodi zu haben: niemals Ausnahmen auslösen, Ausnahmen nur für Kommunikationsfehler auslösen oder Ausnahmen auslösen, wenn ein Befehl keinen "Erfolg" zurückgibt " Indikation.


1

"PasswordNotCorrectException" ist kein gutes Beispiel für die Verwendung von Ausnahmen. Benutzer, die ihre Passwörter falsch eingeben, sind zu erwarten, daher ist dies meiner Meinung nach kaum eine Ausnahme. Sie erholen sich wahrscheinlich sogar davon und zeigen eine nette Fehlermeldung an. Es handelt sich also nur um eine Gültigkeitsprüfung.

Nicht behandelte Ausnahmen stoppen die Ausführung schließlich - was gut ist. Wenn Sie falsche, null oder Fehlercodes zurückgeben, müssen Sie sich selbst mit dem Status des Programms befassen. Wenn Sie vergessen, die Bedingungen irgendwo zu überprüfen, läuft Ihr Programm möglicherweise weiterhin mit falschen Daten, und es fällt Ihnen möglicherweise schwer, herauszufinden, was wo passiert ist .

Natürlich könnten Sie das gleiche Problem mit leeren catch-Anweisungen verursachen, aber zumindest das Erkennen dieser Anweisungen ist einfacher und erfordert nicht, dass Sie die Logik verstehen.

Als Faustregel gilt:

Verwenden Sie sie überall dort, wo Sie einen Fehler nicht möchten oder einfach nicht beheben können.


0

Sie können ein wenig generische Ausnahmen für diese Bedingungen verwenden. Zum Beispiel soll ArgumentException verwendet werden, wenn mit den Parametern einer Methode etwas schief geht (mit Ausnahme von ArgumentNullException). Im Allgemeinen benötigen Sie keine Ausnahmen wie LessThanZeroException, NotPrimeNumberException usw. Denken Sie an den Benutzer Ihrer Methode. Die Anzahl der Bedingungen, die sie speziell behandeln möchte, entspricht der Anzahl der Ausnahmen, die Ihre Methode auslösen muss. Auf diese Weise können Sie bestimmen, wie detailliert Ausnahmen sein werden.

Versuchen Sie übrigens immer, Benutzern Ihrer Bibliotheken einige Möglichkeiten zur Verfügung zu stellen, um Ausnahmen zu vermeiden. TryParse ist ein gutes Beispiel, es existiert, damit Sie nicht int.Parse verwenden und eine Ausnahme abfangen müssen. In Ihrem Fall möchten Sie möglicherweise einige Methoden bereitstellen, um zu überprüfen, ob der Benutzername gültig oder das Kennwort korrekt ist, damit Ihre Benutzer (oder Sie) nicht viele Ausnahmebehandlungen durchführen müssen. Dies führt hoffentlich zu mehr lesbarem Code und einer besseren Leistung.


0

Letztendlich kommt es darauf an, ob es hilfreicher ist, solche Fehler auf Anwendungsebene mithilfe der Ausnahmebehandlung oder über einen eigenen Mechanismus wie die Rückgabe von Statuscodes zu beheben. Ich glaube nicht, dass es eine feste Regel gibt, die besser ist, aber ich würde überlegen:

  • Wer ruft Ihren Code an? Ist dies eine öffentliche API oder eine interne Bibliothek?
  • Welche Sprache benutzt du? Wenn es sich beispielsweise um Java handelt, belastet das Auslösen einer (aktivierten) Ausnahme Ihren Anrufer explizit, um diese Fehlerbedingung auf irgendeine Weise zu behandeln, im Gegensatz zu einem Rückgabestatus, der ignoriert werden könnte. Das könnte gut oder schlecht sein.
  • Wie werden andere Fehlerzustände in derselben Anwendung behandelt? Anrufer möchten sich nicht mit einem Modul befassen, das Fehler auf eigenwillige Weise behandelt, anders als alles andere im System.
  • Wie viele Dinge können mit der fraglichen Routine schief gehen und wie würden sie anders gehandhabt werden? Betrachten Sie den Unterschied zwischen einer Reihe von Catch-Blöcken, die unterschiedliche Fehler behandeln, und dem Einschalten eines Fehlercodes.
  • Haben Sie strukturierte Informationen zu dem Fehler, den Sie zurückgeben müssen? Wenn Sie eine Ausnahme auslösen, können Sie diese Informationen besser platzieren, als nur einen Status zurückzugeben.

0

Es gibt zwei Hauptklassen von Ausnahmen:

1) Systemausnahme (z. B. Datenbankverbindung unterbrochen) oder 2) Benutzerausnahme. (zB Benutzereingabevalidierung, 'Passwort ist falsch')

Ich fand es hilfreich, eine eigene Benutzerausnahmeklasse zu erstellen. Wenn ich einen Benutzerfehler auslösen möchte, möchte ich anders behandelt werden (dh dem Benutzer wird ein Ressourcenfehler angezeigt). In meinem Hauptfehlerbehandler muss ich lediglich den Objekttyp überprüfen ::

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

0

Einige nützliche Dinge, über die Sie nachdenken sollten, wenn Sie entscheiden, ob eine Ausnahme angemessen ist:

  1. Welche Codeebene Sie ausführen möchten, nachdem der Ausnahmekandidat aufgetreten ist - das heißt, wie viele Ebenen des Aufrufstapels sollten abgewickelt werden. Im Allgemeinen möchten Sie eine Ausnahme so nahe wie möglich an der Stelle behandeln, an der sie auftritt. Bei der Überprüfung von Benutzername und Kennwort würden Sie normalerweise Fehler im selben Codeblock behandeln, anstatt eine Ausnahme in die Luft sprudeln zu lassen. Eine Ausnahme ist also wahrscheinlich nicht angebracht. (OTOH, nach drei fehlgeschlagenen Anmeldeversuchen kann sich der Kontrollfluss an eine andere Stelle verschieben, und hier kann eine Ausnahme angebracht sein.)

  2. Ist dieses Ereignis etwas, das Sie in einem Fehlerprotokoll sehen möchten? Nicht jede Ausnahme wird in ein Fehlerprotokoll geschrieben, aber es ist nützlich zu fragen, ob dieser Eintrag in einem Fehlerprotokoll nützlich wäre - dh Sie würden versuchen, etwas dagegen zu tun, oder wäre der Müll, den Sie ignorieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.