it-swarm-eu.dev

Wann soll eine Ausnahme ausgelöst werden?

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

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

Anleitungen oder bewährte Methoden zum Erstellen von Ausnahmen?

405
Kwan Cheng

Meine persönliche Richtlinie lautet: Eine Ausnahme wird ausgelöst, wenn sich herausstellt, 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 ihre Operation keine grauen Bereiche enthält - jede einzelne Klasse erbt oder erbt nicht von List <>, daher lautet die Antwort immer "yes" oder "no".

Beispiel 2: Angenommen, ich habe eine andere Funktion, die eine Liste <> untersucht und true zurückgibt, wenn ihre Länge größer als 50 ist, und false, wenn die Länge kleiner ist. Diese Funktion stellt die Frage: "Enthält diese Liste mehr als 50 Elemente?" Diese Frage geht jedoch davon aus, dass es sich bei dem angegebenen Objekt um eine Liste handelt. Wenn ich NULL gebe, ist diese Annahme falsch. In diesem Fall, wenn die Funktion zurückkehrt entweder wahr oder falsch, dann bricht es seine eigenen Regeln. Die Funktion kann nicht zurückkehren etwas und behaupten, dass es die Frage richtig beantwortet. Es kehrt also nicht zurück - es löst eine Ausnahme aus.

Dies ist vergleichbar mit dem "geladene Frage" logischen Irrtum. Jede Funktion stellt eine Frage. Wenn die eingegebene Eingabe diese Frage zu einem Trugschluss 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 eine Ausnahme ausgelöst werden, anstatt normal zurückzukehren.

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

590

Weil sie Dinge sind, die normalerweise passieren. Ausnahmen sind keine Kontrollflussmechanismen. Benutzer erhalten häufig falsche Passwörter. Dies ist kein Ausnahmefall. Ausnahmen sollten eine wirklich seltene Sache sein, Situationen vom Typ UserHasDiedAtKeyboard.

278
blowdart

Meine kleinen Richtlinien sind stark vom großartigen 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 behoben werden kann
  • Stellen Sie sicher, dass die Ausnahmen auf derselben Abstraktionsebene liegen wie der Rest Ihrer Routine.
  • Ausnahmen sollten für das reserviert werden, was wirklich außergewöhnlich ist.
61
Commander Keen

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, weil man nicht erkennen kann, ob eine Methode eine Ausnahme auslöst, wenn man nur den Aufruf betrachtet. Das ist der Grund, warum Ausnahmen nur verwendet werden sollten, wenn Sie nicht in der Lage sind, angemessen mit der Situation umzugehen (denken Sie an "out of memory" oder "computer is on fire").

34
EricSchaefer

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

25
japollock

Andere schlagen vor, dass Ausnahmen nicht verwendet werden sollten, da die fehlerhafte Anmeldung in einem normalen Ablauf zu erwarten ist, wenn der Benutzer sich vertippt. Ich bin anderer Meinung und verstehe die Argumentation nicht. Vergleichen Sie dies mit dem Öffnen einer Datei. Wenn die Datei nicht existiert 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 Parsing, Webanfragen usw. usw.

Ich betrachte einen schlechten Login nicht als Teil eines normalen Ablaufs, es ist außergewöhnlich. Normalerweise gibt der Benutzer das richtige Kennwort ein, und die Datei ist vorhanden. Die Ausnahmefälle sind außergewöhnlich und es ist vollkommen in Ordnung, Ausnahmen für diese zu verwenden. Die Komplizierung Ihres Codes durch die Weitergabe von Rückgabewerten über n Ebenen im Stapel ist eine Energieverschwendung und führt zu unordentlichem Code. Tun Sie das Einfachste, was möglicherweise funktionieren könnte. Optimieren Sie nicht vorzeitig, indem Sie Fehlercodes verwenden. Ausnahmefehler treten per Definition selten auf, und Ausnahmen kosten nichts, es sei denn, Sie werfen sie.

23
Bjorn Reppen

Ausnahmen sind etwas kostspielig. Wenn Sie beispielsweise einen Benutzer haben, der ein ungültiges Kennwort angibt, ist es in der Regel besser, ein Fehlerflag oder einen anderen ungültigen Indikator zurückzugeben.

Dies ist auf die Art und Weise zurückzuführen, in der Ausnahmen behandelt werden, dass echte Fehleingaben und eindeutige kritische Stoppelemente Ausnahmen, aber keine fehlgeschlagenen Anmeldeinformationen sein sollten.

16
Mitchel Sellers

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 Arbeitsspeicher zuweisen und es keine zuzuweisenden Arbeitsspeicher gibt. In den von Ihnen genannten Fällen können Sie sich eindeutig von diesen Zuständen erholen und Ihrem Anrufer einen entsprechenden Fehlercode zurückgeben.


Sie werden viele Ratschläge erhalten, auch bei der Beantwortung dieser 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, weil 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++ im Artikel von Dr. Dobbs Wann und wie man Ausnahmen verwendet und auch in seinem Buch mit Andrei Alexandrescu, C++ Coding Standards): Wirf genau dann eine Ausnahme, wenn

  • eine Vorbedingung 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 Gründen besser.

  • Vorbedingungen, Nachbedingungen und Invarianten sind design Merkmale unseres Programms (seiner internen API), während die Entscheidung für throw 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 Design-Einschränkungen erfüllt.
  • Es zwingt uns, in Vorbedingungen, Nachbedingungen und Invarianten zu denken, die only Annahmen sind, die Aufrufer unserer Methode treffen und genau ausdrücken sollten, 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 testbar; Es resultiert ein Code, der leicht auf Einheit getestet werden kann, da die Nachbedingungen Prädikate sind, die unser Einheit-Test-Code prüfen kann (assert).
  • Das Denken in Nachbedingungen erzeugt natürlich ein Design, das Erfolg als Nachbedingung hat, was der natürliche Stil für die Verwendung von Ausnahmen ist. Der normale ("happy") Ausführungspfad Ihres Programms ist linear angeordnet, wobei der gesamte Fehlerbehandlungscode in die catch -Klauseln verschoben wird.
14
Jon

Ich würde sagen, dass es keine festen Regeln für die Verwendung von Ausnahmen gibt. 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 Parameter "out" erreicht werden kann)
  • Sprachen bieten im Allgemeinen einige Funktionen zum Verwalten einer ordentlichen Bereinigung im Ausnahmefall (try/finally in Java unter Verwendung von in 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 gegen sein kann)

Gründe, keine Ausnahmen zu verwenden:

  • Manchmal ist es übertrieben, wenn die Fehlerbehandlung einfach ist
  • Wenn Ausnahmen nicht dokumentiert oder deklariert sind, werden sie möglicherweise vom aufrufenden Code abgefangen. Dies kann schlimmer sein, als wenn der aufrufende Code nur einen Rückkehrcode ignoriert (Anwendungs-Exit oder unbeaufsichtigter Fehler - was schlimmer ist, hängt vom Szenario ab).
  • In C++ muss Code, der Ausnahmen verwendet, ausnahmesicher sein (auch wenn Sie sie nicht werfen oder abfangen, sondern indirekt eine Auslösefunktion aufrufen).
  • In C++ ist es schwer zu sagen, wann eine Funktion ausgelöst werden könnte, daher müssen Sie paranoid in Bezug auf die Ausnahmesicherheit sein, wenn Sie sie verwenden
  • Das Auslösen und Fangen von Ausnahmen ist im Allgemeinen erheblich teurer als das Überprüfen einer Rückkehrflagge

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 von ist Der größte Vorteil der Verwendung in Java IMO, ist, dass Sie wissen, dass Ihr Anrufer die Ausnahme behandeln MUSS, und dies verbessert die Wahrscheinlichkeit von korrektes Verhalten.

Aus diesem Grund würde ich in jeder Sprache immer alle Ausnahmen in einer Code- oder API-Ebene von einer gemeinsamen Klasse ableiten, sodass der Aufruf von Code immer garantieren kann, dass alle Ausnahmen abgefangen werden. Ich würde es auch als schlecht betrachten, beim Schreiben einer API oder Bibliothek Ausnahmeklassen auszulösen, die implementierungsspezifisch sind (d. H. Ausnahmen aus 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 in der zurückzuführen ist Programm.

10
Robert

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 von ihnen zu "allgemeineren" Ausnahmen zusammenfassen (z. B. in Ihrem Fall "AuthenticationFailedException") und anhand der Ausnahmemeldung detaillieren, was schief gelaufen ist. Sofern Ihr Code sie nicht unterschiedlich behandelt, müssen Sie nicht viele Ausnahmeklassen erstellen. Und wenn ja, sollten Sie einfach eine Aufzählung mit dem aufgetretenen Fehler zurückgeben. Auf diese Weise ist es ein bisschen sauberer.

5
Shachar

Wenn es sich um Code handelt, der in einer Schleife ausgeführt wird, die 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 so ähnlich. BaseException erbt System.Exception, aber alle Ihre Ausnahmen erben BaseException. Sie können sogar einen Baum von Ausnahmetypen erstellen, um ähnliche Typen zu gruppieren. Dies kann jedoch zu viel oder zu wenig sein.

Die kurze Antwort lautet also: Wenn dies keine nennenswerten Leistungseinbußen zur Folge hat (was nicht der Fall sein sollte, wenn Sie nicht viele Ausnahmen machen), fahren Sie fort.

5
Charles Graham

Ich bin mit japollock weit oben einverstanden. Werfen Sie eine Bestätigung, wenn Sie sich über das Ergebnis einer Operation nicht sicher sind. Aufrufe von APIs, Zugriff auf Dateisysteme, Datenbankaufrufe usw. Immer dann, wenn Sie die "Grenzen" Ihrer Programmiersprachen überschreiten.

Ich möchte hinzufügen, zögern Sie nicht, eine Standardausnahme zu werfen. Es sei denn, Sie werden etwas "anderes" tun (ignorieren, mailen, protokollieren, das Twitter-Walbild-Ding zeigen usw.), dann kümmern Sie sich nicht um benutzerdefinierte Ausnahmen.

3
dclaysmith

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 Verarbeitung, die bis zu diesem Zeitpunkt stattgefunden hat, nicht rückgängig machen können, müssen Sie sie beenden. Was können Sie sonst noch tun? Ihre Verarbeitungslogik wird irgendwann anderswo versagen. Wenn du dich irgendwie erholen kannst, dann mach das und wirf keine Ausnahme.

in Ihrem speziellen Fall sollten Sie, wenn Sie gezwungen waren, etwas Dummes zu tun, wie Geldabheben zu akzeptieren, und erst dann Benutzer/Passwort überprüfen, den Vorgang abbrechen, indem Sie eine Ausnahme auslösen, um zu benachrichtigen, dass etwas Schlimmes passiert ist, und weiteren Schaden zu verhindern.

3
goran

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

Sie würden sicherlich nicht mit einem ausnahmebedingten Flow 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, einen Ausnahmetyp für jede Ausnahmesituation zu erstellen. Das ist nicht produktiv.

Im Allgemeinen bevorzuge ich es, zwei grundlegende Ausnahmetypen zu erstellen, die systemweit verwendet werden: LogicalException und TechnicalException. Diese können bei Bedarf weiter nach Subtypen unterschieden werden, sind aber im Allgemeinen nicht erforderlich.

Die technische Ausnahme gibt die wirklich unerwartete Ausnahme an, z. B. dass der Datenbankserver ausfällt, die Verbindung zum Webdienst die IOException ausgelöst hat und so weiter.

Andererseits werden die logischen Ausnahmen verwendet, um die weniger schwerwiegende fehlerhafte Situation auf die oberen Schichten zu übertragen (im Allgemeinen einige Validierungsergebnisse).

Bitte beachten Sie, dass auch die logische Ausnahme nicht regelmäßig zur Steuerung des Programmablaufs verwendet werden soll, sondern vielmehr zur Hervorhebung der Situation, 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 kann es daher sinnvoll sein, so etwas wie AuthenticationException zu erstellen und die konkreten Situationen durch Aufzählungswerte wie sernameNotExisting, PasswordMismatch usw. zu unterscheiden. Dann landen Sie nicht in Sie haben eine große Ausnahmehierarchie und können die Catch-Blöcke auf einem wartbaren Niveau halten. Sie können auch leicht einen generischen Ausnahmebehandlungsmechanismus anwenden, 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 Exception wird auf das SOAPFault-Detail gemarshallt und dann auf dem Client wieder auf die Exception gemarshallt. Dies führt dazu, dass der Validierungsfehler in einem bestimmten Webseiten-Eingabefeld angezeigt wird, da die Exception 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 im Falle eines Ausfalls) - es liegt in Ihrem Ermessen.

2
Petr Macek

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

2
Joe Skora

Im Allgemeinen möchten Sie eine Ausnahme für alle Ereignisse in Ihrer Anwendung auslösen, die "Außergewöhnlich" sind.

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

Sie stellen "Ausnahmen" für den Hauptfluss Ihrer UML dar, sind jedoch eher "Verzweigungen" in der Verarbeitung.

Wenn Sie versucht haben, auf Ihre passwd-Datei oder -Datenbank zuzugreifen, dies jedoch nicht möglich ist, ist dies ein Ausnahmefall, der das Auslösen einer Ausnahme rechtfertigt.

2
Gord

Erstens, wenn die Benutzer Ihrer API nicht an bestimmten, detaillierten Fehlern interessiert sind, sind bestimmte Ausnahmen für sie nicht von Nutzen.

Da Sie häufig nicht wissen können, was für Ihre Benutzer nützlich sein kann, sollten Sie die spezifischen Ausnahmen besser kennen, aber sicherstellen, dass sie von einer gemeinsamen Klasse erben (z. B. std :: exception oder deren Ableitungen in C++). Auf diese Weise kann Ihr Kunde bestimmte Ausnahmen abfangen, wenn er dies wünscht, oder die allgemeinere Ausnahme, wenn es ihn nicht interessiert.

2
Jason Etheridge

Ich habe philosophische Probleme mit der Verwendung von Ausnahmen. Grundsätzlich erwarten Sie, dass ein bestimmtes Szenario eintritt, aber anstatt es explizit zu behandeln, stoßen Sie das Problem ab, um es "woanders" zu behandeln. Und wo das "anderswo" ist, kann jeder raten.

2
Dan

Ich habe drei Arten von Zuständen, die ich fange.

  1. Fehlerhafte oder fehlende Eingaben sollten keine Ausnahme sein. Verwenden Sie sowohl Client-Side-Js als auch Server-Side-Regex, um Attribute zu erkennen, festzulegen und mit Nachrichten auf dieselbe Seite zurückzukehren.

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

  3. Die unerwartete Ausnahme. Dies sind diejenigen, die Sie nicht kennen. Protokollieren Sie es mit Details und leiten Sie es an eine allgemeine Fehlerseite weiter.

Hoffe das hilft

1
Michael

für mich sollte eine Ausnahme ausgelöst werden, wenn eine erforderliche technische oder geschäftliche Regel fehlschlägt. Wenn zum Beispiel eine Auto-Entität einem Array von 4 Reifen zugeordnet ist ... wenn ein Reifen oder mehrere Reifen null sind ... sollte eine Ausnahme "NotEnoughTiresException" ausgelöst werden, da sie auf verschiedenen Ebenen des Systems abgefangen werden kann und eine signifikante hat Sinn durch Protokollierung. Außerdem, wenn wir nur versuchen, die Null zu kontrollieren und die Instanziation des Autos zu verhindern. Möglicherweise werden wir die Ursache des Problems nie finden, da der Reifen in erster Linie nicht null sein soll.

1
Genjuro

Die einfache Antwort lautet, wann immer eine Operation unmöglich ist (wegen einer der beiden Anwendungen OR), weil sie die Geschäftslogik verletzen würde.) Wenn eine Methode aufgerufen wird und es unmöglich ist, das zu tun, wofür die Methode geschrieben wurde Ein gutes Beispiel ist, dass Konstruktoren immer ArgumentExceptions auslösen, wenn eine Instanz mit den angegebenen Parametern nicht erstellt werden kann. Ein weiteres Beispiel ist InvalidOperationException, die ausgelöst wird, wenn eine Operation aufgrund des Status eines anderen Mitglieds oder von Mitgliedern des nicht ausgeführt werden kann Klasse.

Wenn in Ihrem Fall eine Methode wie Login (Benutzername, Passwort) aufgerufen wird und der Benutzername ungü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 (d. H. Es ist nicht möglich, da dies die Authentifizierung verletzen würde). Daher muss eine Ausnahme ausgelöst werden. Obwohl ich möglicherweise Ihre beiden Ausnahmen von ArgumentException erben habe.

Wenn Sie jedoch KEINE Ausnahme auslösen möchten, weil ein Anmeldefehler sehr häufig ist, 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 Overheads zu vermeiden, der durch das Auslösen der Ausnahmen entsteht. Es ist großartig, ressourcenbewusst zu sein, geht jedoch in der Regel nicht zu Lasten Ihres Anwendungsdesigns. Das ist wahrscheinlich der Grund, warum Sie angewiesen wurden, Ihre beiden Ausnahmen nicht zu werfen. Ob Ausnahmen verwendet werden sollen oder nicht, hängt in der Regel davon ab, wie häufig die Ausnahme auftritt. Wenn es sich um ein ziemlich verbreitetes oder ein ziemlich zu erwartendes Ergebnis handelt, vermeiden die meisten Entwickler Ausnahmen und erstellen stattdessen eine andere Methode, um auf einen Fehler hinzuweisen, der auf den vermuteten Ressourcenverbrauch zurückzuführen ist.

Im Folgenden finden Sie ein Beispiel für die Vermeidung der Verwendung von Ausnahmen in einem Szenario wie dem oben beschriebenen 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
core

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 eintritt. Wenn Sie nicht wissen, haben Sie entweder eine separate doSomething- und trySomething-Methode, bei der die erste einen Fehler zurückgibt und die zweite keine oder eine Routine, die einen Parameter akzeptiert, der angibt, ob eine Ausnahme ausgelöst werden soll, wenn sie fehlschlägt. Stellen Sie sich eine Klasse vor, um Befehle an ein fernes System zu senden und Antworten zu melden. Bestimmte Befehle (z. B. Neustart) veranlassen das ferne System, eine Antwort zu senden, reagieren dann jedoch für eine bestimmte Zeitspanne nicht. Es ist daher nützlich, in der Lage zu sein, einen "Ping" -Befehl zu senden und herauszufinden, ob das ferne System in angemessener Zeit reagiert, ohne eine Ausnahme auslösen zu müssen, wenn dies nicht der Fall ist (der Anrufer würde wahrscheinlich erwarten, dass die ersten " ping "versuche würden scheitern, aber irgendwann würde man klappen). Auf der anderen Seite, wenn man eine Folge von Befehlen hat wie:

 exchange_command ("open tempfile"); 
 exchange_command ("write tempfile data {whatever}"); 
 exchange_command ("write tempfile data {whatever}"); 
 exchange_command ("write tempfile data {whatever}"); 
 exchange_command ("write tempfile data {whatever}"); 
 exchange_command ("close tempfile"); 
 exchange_command ("copy tempfile to realfile"); 

man möchte, dass ein Fehlschlag einer Operation die gesamte Sequenz abbricht. Man könnte zwar jede Operation überprüfen, um sicherzustellen, dass sie erfolgreich war, aber es ist 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
supercat

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

Eine Sache, die der Artikel unten angibt, 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
Sam

Die Sicherheit wird mit Ihrem Beispiel in Konflikt gebracht: Sie sollten einem Angreifer nicht mitteilen, dass ein Benutzername existiert, aber das Passwort ist falsch. Das sind zusätzliche Informationen, die Sie nicht weitergeben müssen. Sagen Sie einfach "der Benutzername oder das Passwort ist falsch."

1
anon

Das Auslösen von Ausnahmen führt dazu, dass sich der Stapel auflöst, was einige Leistungseinbußen zur Folge hat (zugegebenermaßen haben sich moderne verwaltete Umgebungen dahingehend verbessert). Immer wieder Ausnahmen in einer verschachtelten Situation zu werfen und zu fangen, wäre eine schlechte Idee.

Wahrscheinlich wichtiger als das, sind Ausnahmen 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
Arno

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

  1. welche Codeebene soll ausgeführt werden, nachdem der Ausnahmekandidat aufgetreten ist? Das heißt, wie viele Ebenen des Aufrufstapels sollten sich auflösen. 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 Benutzernamen und Kennwort werden Fehler normalerweise im selben Codeblock behandelt, anstatt dass eine Ausnahme ausgelöst wird. Eine Ausnahme ist also wahrscheinlich nicht angebracht. (Nach drei fehlgeschlagenen Anmeldeversuchen kann sich der Steuerungsfluss 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 - d. H. Sie würden versuchen, etwas dagegen zu unternehmen, oder es wäre der Müll, den Sie ignorieren.

0
Mike Kantor

Es gibt zwei Hauptklassen von Ausnahmen:

1) Systemausnahme (z. B. Datenbankverbindung unterbrochen) oder 2) Benutzerausnahme. (zB Überprüfung der Benutzereingaben, 'Passwort ist falsch')

Ich fand es hilfreich, eine eigene Benutzerausnahmeklasse zu erstellen, und wenn ich einen Benutzerfehler auslösen möchte, der anders behandelt werden soll (dh ein dem Benutzer angezeigter Ressourcenfehler), muss ich in meinem Hauptfehlerhandler nur 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
Crusty

"PasswordNotCorrectException" ist kein gutes Beispiel für die Verwendung von Ausnahmen. Es ist zu erwarten, dass Benutzer ihre Passwörter falsch eingeben, daher ist dies meiner Meinung nach kaum eine Ausnahme. Sie können sich wahrscheinlich sogar davon erholen, indem Sie eine nette Fehlermeldung anzeigen, es ist also nur eine Gültigkeitsprüfung.

Nicht behandelte Ausnahmen stoppen die Ausführung schließlich - was gut ist. Wenn Sie Falsch-, Null- oder Fehlercodes zurückgeben, müssen Sie den Status des Programms selbst bestimmen. Wenn Sie vergessen, die Bedingungen irgendwo zu überprüfen, wird Ihr Programm möglicherweise weiterhin mit falschen Daten ausgeführt, und es fällt Ihnen möglicherweise schwer, herauszufinden, was passiert ist und wo.

Natürlich könnten Sie das gleiche Problem mit leeren catch-Anweisungen verursachen, aber zumindest ist es einfacher, diese zu erkennen, und Sie müssen die Logik nicht verstehen.

Also als Faustregel:

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

0
DanMan

Sie können ein wenig allgemeine Ausnahmen für diese Bedingungen verwenden. Für z.B. ArgumentException soll 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 festlegen, wie detailliert Ausnahmen sein werden.

Versuchen Sie im Übrigen immer, Benutzern Ihrer Bibliotheken einige Möglichkeiten zur Verfügung zu stellen, um Ausnahmen zu vermeiden. TryParse ist ein gutes Beispiel. Es ist vorhanden, damit Sie int.Parse nicht 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 wird hoffentlich zu mehr lesbarem Code und einer besseren Leistung führen.

0
Serhat Ozgel

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

  • Wer ruft Ihren Code an? Handelt es sich um eine öffentliche API oder um eine interne Bibliothek?
  • Welche sprache sprichst du Wenn es sich zum Beispiel um Java handelt, belastet das Auslösen einer (aktivierten) Ausnahme Ihren Aufrufer explizit, um diese Fehlerbedingung in irgendeiner Weise zu behandeln, im Gegensatz zu einem Rückgabestatus, der ignoriert werden könnte. Das kann gut oder schlecht sein.
  • Wie werden andere Fehlerbedingungen in derselben Anwendung behandelt? Anrufer möchten sich nicht mit einem Modul befassen, das Fehler auf eine eigenwillige Art und Weise behandelt, die mit nichts anderem im System zu vergleichen ist.
  • Wie viele Dinge können mit der fraglichen Routine schief gehen und wie würden sie anders gehandhabt? 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 speichern als nur einen Status zurückgeben.
0
eli