Vertragsgestaltung mit Behauptungen oder Ausnahmen? [geschlossen]


123

Bei der vertraglichen Programmierung prüft eine Funktion oder Methode zunächst, ob ihre Voraussetzungen erfüllt sind, bevor sie mit der Bearbeitung ihrer Verantwortlichkeiten beginnt, oder? Die beiden bekanntesten Methoden für diese Überprüfungen sind nach assertund nach exception.

  1. assert schlägt nur im Debug-Modus fehl. Um sicherzustellen, dass es wichtig ist, alle separaten Vertragsvoraussetzungen (auf Einheit) zu testen, um festzustellen, ob sie tatsächlich fehlschlagen.
  2. Ausnahme schlägt im Debug- und Release-Modus fehl. Dies hat den Vorteil, dass das getestete Debug-Verhalten mit dem Release-Verhalten identisch ist, jedoch eine Beeinträchtigung der Laufzeitleistung zur Folge hat.

Welches ist Ihrer Meinung nach vorzuziehen?

Siehe releated Frage hier


3
Der ganze Punkt hinter Design by Contract ist, dass Sie die Voraussetzungen zur Laufzeit nicht überprüfen müssen (und sollten). Sie überprüfen die Eingabe, bevor Sie sie mit den Voraussetzungen an die Methode übergeben. So respektieren Sie Ihr Vertragsende. Wenn die Eingabe ungültig ist oder Ihr Vertragsende verletzt, schlägt das Programm in der Regel ohnehin aufgrund seiner normalen Vorgehensweise (die Sie möchten) fehl.
void.pointer

Schöne Frage, aber ich denke, Sie sollten wirklich die akzeptierte Antwort wechseln (wie die Stimmen auch zeigen)!
DaveFar

Für immer später, ich weiß, aber sollte diese Frage tatsächlich das c ++ - Tag haben? Ich habe nach dieser Antwort gesucht, um sie in einer anderen Sprache (Delpih) zu verwenden, und ich kann mir keine Sprache vorstellen, die Ausnahmen und Behauptungen enthält, die nicht denselben Regeln folgen würden. (Lernen noch Stapelüberlauf-Richtlinien.)
Eric G

Sehr prägnante Antwort in dieser Antwort : "Mit anderen Worten, Ausnahmen betreffen die Robustheit Ihrer Anwendung, während Behauptungen die Richtigkeit betreffen."
Shmuel Levine

Antworten:


39

Das Deaktivieren von Assert in Release-Builds entspricht der Aussage "Ich werde in einem Release-Build keinerlei Probleme haben", was häufig nicht der Fall ist. Assert sollte also in einem Release-Build nicht deaktiviert werden. Sie möchten aber auch nicht, dass der Release-Build abstürzt, wenn Fehler auftreten, oder?

Verwenden Sie also Ausnahmen und verwenden Sie sie gut. Verwenden Sie eine gute, solide Ausnahmehierarchie und stellen Sie sicher, dass Sie die Ausnahme abfangen und in Ihren Debugger einbinden, um sie abzufangen. Im Release-Modus können Sie den Fehler kompensieren und nicht einen direkten Absturz. Es ist der sicherere Weg.


4
Behauptungen sind zumindest in Fällen nützlich, in denen die Überprüfung der Richtigkeit entweder ineffizient oder ineffizient wäre, um sie ordnungsgemäß zu implementieren.
Casebash

89
Bei Behauptungen geht es nicht darum, Fehler zu korrigieren, sondern den Programmierer zu alarmieren. Es ist aus diesem Grund nutzlos , sie in Release-Builds aktiviert zu lassen : Was hätten Sie durch ein Assert-Firing gewonnen? Der Entwickler kann nicht hineinspringen und es debuggen. Zusicherungen sind eine Debugging-Hilfe, sie ersetzen keine Ausnahmen (und Ausnahmen ersetzen auch keine Zusicherungen). Ausnahmen machen das Programm auf einen Fehlerzustand aufmerksam. Assert benachrichtigt den Entwickler.
Jalf

12
Eine Zusicherung sollte jedoch verwendet werden, wenn die internen Daten nach dem Fixieren beschädigt wurden. Wenn eine Zusicherung ausgelöst wird, können Sie keine Annahmen über den Status des Programms treffen, da dies bedeutet, dass etwas / falsch / ist. Wenn eine Zusicherung fehlgeschlagen ist, können Sie nicht davon ausgehen, dass Daten gültig sind. Aus diesem Grund sollte ein Release-Build behaupten, dem Programmierer nicht zu sagen, wo das Problem liegt, sondern damit das Programm heruntergefahren werden kann und keine größeren Probleme riskiert werden. Das Programm sollte einfach alles tun, um die Wiederherstellung später zu erleichtern, wenn den Daten vertraut werden kann.
Coppro

5
@jalf, Obwohl Sie in Release-Builds keinen Haken in Ihren Debugger setzen können, können Sie die Protokollierung nutzen, damit Entwickler sehen, dass die für Ihre Behauptung relevanten Informationen fehlschlagen. In diesem Dokument ( martinfowler.com/ieeeSoftware/failFast.pdf ) weist Jim Shore darauf hin: "Denken Sie daran, dass ein Fehler beim Kunden den Testprozess durchlaufen hat. Sie haben wahrscheinlich Probleme, ihn zu reproduzieren. Diese Fehler sind Das am schwersten zu findende und eine gut platzierte Behauptung, die das Problem erklärt, könnte Ihnen Tage Mühe ersparen. "
StriplingWarrior

5
Persönlich bevorzuge ich Behauptungen für die Gestaltung durch Vertragsansätze. Ausnahmen sind defensiv und führen die Argumentprüfung innerhalb der Funktion durch. Außerdem sagen die dbc-Voraussetzungen nicht "Ich werde nicht arbeiten, wenn Sie Werte außerhalb des Arbeitsbereichs verwenden", sondern "Ich kann nicht garantieren, dass die richtige Antwort gegeben wird, aber ich kann es trotzdem tun". Die Asserts geben dem Entwickler das Feedback, dass er eine Funktion mit einer Bedingungsverletzung aufruft. Halten Sie ihn jedoch nicht davon ab, sie zu verwenden, wenn er das Gefühl hat, es besser zu wissen. Der Verstoß könnte dazu führen, dass Ausnahmen auftreten, aber ich sehe das als etwas anderes.
Matt_JD

194

Als Faustregel gilt, dass Sie Behauptungen verwenden sollten, wenn Sie versuchen, Ihre eigenen Fehler zu erfassen, und Ausnahmen, wenn Sie versuchen, die Fehler anderer Personen zu erfassen. Mit anderen Worten, Sie sollten Ausnahmen verwenden, um die Voraussetzungen für die öffentlichen API-Funktionen zu überprüfen und wann immer Sie Daten erhalten, die außerhalb Ihres Systems liegen. Sie sollten Asserts für die Funktionen oder Daten verwenden, die systemintern sind.


Was ist mit Serialisieren / Deserialisieren, wenn Sie in verschiedenen Modulen / Anwendungen sitzen und schließlich nicht mehr synchron sind? Ich meine, seitens des Lesers ist es immer mein Fehler, wenn ich versuche, die Dinge falsch zu lesen, so dass ich eher Asserts verwende, aber andererseits habe ich externe Daten, die das Format möglicherweise ohne Vorankündigung ändern können.
Slava

Wenn die Daten extern sind, sollten Sie Ausnahmen verwenden. In diesem speziellen Fall sollten Sie diese Ausnahmen wahrscheinlich auch abfangen und auf vernünftige Weise behandeln, anstatt nur Ihr Programm sterben zu lassen. Meine Antwort ist auch eine Faustregel, kein Naturgesetz. :) Man muss also jeden Fall einzeln betrachten.
Dima

Wenn Ihre Funktion f (int * x) eine Zeile x-> len enthält, stürzt f (v), bei dem v nachweislich null ist, garantiert ab. Wenn v noch früher als null erwiesen ist und f (v) als aufgerufen wird, liegt ein logischer Widerspruch vor. Es ist dasselbe wie bei a / b, wobei b letztendlich als 0 erwiesen ist. Idealerweise sollte ein solcher Code nicht kompiliert werden können. Das Deaktivieren von Annahmeprüfungen ist völlig dumm, es sei denn, es geht um die Kosten der Prüfungen, da dadurch der Ort verdeckt wird, an dem eine Annahme verletzt wurde. Es muss mindestens protokolliert werden. Sie sollten ohnehin ein Neustart-bei-Absturz-Design haben.
Rob

22

Das Prinzip, dem ich folge, lautet: Wenn eine Situation durch Codierung realistisch vermieden werden kann, verwenden Sie eine Behauptung. Verwenden Sie andernfalls eine Ausnahme.

Aussagen dienen dazu, sicherzustellen, dass der Vertrag eingehalten wird. Der Vertrag muss fair sein, damit der Kunde in der Lage sein muss, die Einhaltung sicherzustellen. Beispielsweise können Sie in einem Vertrag angeben, dass eine URL gültig sein muss, da die Regeln darüber, was eine gültige URL ist und was nicht, bekannt und konsistent sind.

Ausnahmen gelten für Situationen, die außerhalb der Kontrolle des Clients und des Servers liegen. Eine Ausnahme bedeutet, dass etwas schief gelaufen ist und nichts hätte getan werden können, um dies zu vermeiden. Die Netzwerkkonnektivität liegt beispielsweise außerhalb der Anwendungssteuerung, sodass nichts unternommen werden kann, um einen Netzwerkfehler zu vermeiden.

Ich möchte hinzufügen, dass die Unterscheidung zwischen Behauptung und Ausnahme nicht wirklich die beste Art ist, darüber nachzudenken. Woran Sie wirklich denken möchten, ist der Vertrag und wie er durchgesetzt werden kann. In meinem obigen URL-Beispiel ist es am besten, eine Klasse zu haben, die eine URL kapselt und entweder Null oder eine gültige URL ist. Es ist die Konvertierung einer Zeichenfolge in eine URL, die den Vertrag erzwingt, und eine Ausnahme wird ausgelöst, wenn sie ungültig ist. Eine Methode mit einem URL-Parameter ist viel klarer als eine Methode mit einem String-Parameter und einer Zusicherung, die eine URL angibt.


6

Behauptungen dienen dazu, etwas zu fangen, das ein Entwickler falsch gemacht hat (nicht nur Sie selbst - auch ein anderer Entwickler in Ihrem Team). Wenn es vernünftig ist, dass ein Benutzerfehler diese Bedingung verursachen könnte, sollte dies eine Ausnahme sein.

Denken Sie auch über die Konsequenzen nach. Ein Assert fährt normalerweise die App herunter. Wenn realistische Erwartungen bestehen, dass der Zustand behoben werden kann, sollten Sie wahrscheinlich eine Ausnahme verwenden.

Wenn das Problem jedoch nur auf einen Programmiererfehler zurückzuführen ist, verwenden Sie eine Bestätigung, da Sie dies so schnell wie möglich wissen möchten. Eine Ausnahme könnte abgefangen und behandelt werden, und Sie würden nie davon erfahren. Und ja, Sie sollten Asserts im Release-Code deaktivieren, da die App dort wiederhergestellt werden soll, wenn die geringste Wahrscheinlichkeit besteht. Selbst wenn der Status Ihres Programms stark beeinträchtigt ist, kann der Benutzer möglicherweise seine Arbeit speichern.


5

Es ist nicht genau richtig, dass "Assert nur im Debug-Modus fehlschlägt".

In Object Oriented Software Construction, 2. Auflage von Bertrand Meyer, lässt der Autor eine Tür offen, um die Voraussetzungen im Release-Modus zu überprüfen. In diesem Fall passiert, wenn eine Behauptung fehlschlägt, dass ... eine Ausnahme für eine Assertionsverletzung ausgelöst wird! In diesem Fall gibt es keine Wiederherstellung nach der Situation: Es könnte jedoch etwas Nützliches getan werden. Es besteht darin, automatisch einen Fehlerbericht zu generieren und in einigen Fällen die Anwendung neu zu starten.

Die Motivation dahinter ist, dass Voraussetzungen in der Regel billiger zu testen sind als Invarianten und Nachbedingungen, und dass in einigen Fällen Korrektheit und "Sicherheit" im Release-Build wichtiger sind als Geschwindigkeit. dh Für viele Anwendungen ist Geschwindigkeit kein Problem, sondern Robustheit (die Fähigkeit des Programms, sich sicher zu verhalten, wenn sein Verhalten nicht korrekt ist, dh wenn ein Vertrag gebrochen wird).

Sollten Sie die Vorbedingungsprüfungen immer aktiviert lassen? Es hängt davon ab, ob. Es liegt an dir. Es gibt keine universelle Antwort. Wenn Sie Software für eine Bank erstellen, ist es möglicherweise besser, die Ausführung mit einer alarmierenden Meldung zu unterbrechen, als 1.000.000 USD anstelle von 1.000 USD zu überweisen. Aber was ist, wenn Sie ein Spiel programmieren? Vielleicht brauchen Sie die Geschwindigkeit, die Sie bekommen können, und wenn jemand 1000 statt 10 Punkte wegen eines Fehlers bekommt, den die Voraussetzungen nicht erkannt haben (weil sie nicht aktiviert sind), dann haben Sie Pech.

In beiden Fällen sollten Sie diesen Fehler idealerweise während des Testens erkannt haben, und Sie sollten einen wesentlichen Teil Ihrer Tests mit aktivierten Zusicherungen durchführen. Was hier diskutiert wird, ist die beste Richtlinie für die seltenen Fälle, in denen Voraussetzungen im Produktionscode in einem Szenario fehlschlagen, das aufgrund unvollständiger Tests nicht früher erkannt wurde.

Zusammenfassend lässt sich sagen , dass Sie Zusicherungen haben und die Ausnahmen trotzdem automatisch erhalten können , wenn Sie sie aktiviert lassen - zumindest in Eiffel. Ich denke, um dasselbe in C ++ zu tun, müssen Sie es selbst eingeben.

Siehe auch: Wann sollten Zusicherungen im Produktionscode bleiben?


1
Ihr Punkt ist definitiv gültig. Die SO nicht eine bestimmte Sprache angeben - im Fall von C # die Standard - Assertion ist System.Diagnostics.Debug.Assert, die nicht nur in einem Debug - Build fehl, und wird bei der Kompilierung in einem Releasebuild entfernt werden.
Jojo

2

Es gab einen großen Thread bezüglich des Aktivierens / Deaktivierens von Behauptungen in Release-Builds auf comp.lang.c ++. Moderiert. Wenn Sie ein paar Wochen Zeit haben, können Sie sehen, wie unterschiedlich die Meinungen dazu sind. :) :)

Im Gegensatz zu coppro glaube ich, dass wenn Sie nicht sicher sind, ob eine Zusicherung in einem Release-Build deaktiviert werden kann, es keine Zusicherung sein sollte. Behauptungen sollen vor dem Bruch von Programminvarianten schützen. In einem solchen Fall gibt es für den Client Ihres Codes eines von zwei möglichen Ergebnissen:

  1. Sterben Sie mit einem Betriebssystemfehler, der zu einem Abbruch führt. (Ohne zu behaupten)
  2. Stirb durch einen direkten Anruf zum Abbruch. (Mit Behauptung)

Es gibt keinen Unterschied für den Benutzer. Es ist jedoch möglich, dass die Zusicherungen unnötige Leistungskosten für den Code verursachen, der in den allermeisten Läufen vorhanden ist, in denen der Code nicht fehlschlägt.

Die Antwort auf die Frage hängt viel mehr davon ab, wer die Clients der API sein werden. Wenn Sie eine Bibliothek schreiben, die eine API bereitstellt, benötigen Sie einen Mechanismus, um Ihre Kunden darüber zu informieren, dass sie die API falsch verwendet haben. Wenn Sie nicht zwei Versionen der Bibliothek angeben (eine mit Asserts, eine ohne), ist Assert sehr unwahrscheinlich die richtige Wahl.

Persönlich bin ich mir jedoch nicht sicher, ob ich auch in diesem Fall Ausnahmen machen würde. Ausnahmen sind besser geeignet, wenn eine geeignete Form der Wiederherstellung stattfinden kann. Beispielsweise können Sie versuchen, Speicher zuzuweisen. Wenn Sie eine 'std :: bad_alloc'-Ausnahme abfangen, können Sie möglicherweise Speicher freigeben und es erneut versuchen.


2

Ich habe hier meine Sicht auf den Stand der Dinge skizziert: Wie validieren Sie den internen Zustand eines Objekts? . Im Allgemeinen behaupten Sie Ihre Ansprüche und werfen Sie für die Verletzung durch andere. Zum Deaktivieren von Asserts in Release-Builds haben Sie folgende Möglichkeiten:

  • Deaktivieren Sie Asserts für teure Überprüfungen (z. B. Überprüfen, ob ein Bereich bestellt wurde).
  • Lassen Sie triviale Überprüfungen aktiviert (z. B. die Überprüfung auf einen Nullzeiger oder einen booleschen Wert).

Natürlich sollten in Release-Builds fehlgeschlagene Zusicherungen und nicht erfasste Ausnahmen anders behandelt werden als in Debug-Builds (wo nur std :: abort aufgerufen werden kann). Schreiben Sie irgendwo ein Protokoll des Fehlers (möglicherweise in eine Datei) und teilen Sie dem Kunden mit, dass ein interner Fehler aufgetreten ist. Der Kunde kann Ihnen die Protokolldatei senden.


1

Sie fragen nach dem Unterschied zwischen Entwurfs- und Laufzeitfehlern.

Behauptungen sind "Hey Programmierer, das ist kaputt" -Nachrichten. Sie sollen Sie an Fehler erinnern, die Sie nicht bemerkt hätten, als sie aufgetreten sind.

Ausnahmen sind "Hey Benutzer, etwas ist schief gelaufen" -Benachrichtigungen (natürlich können Sie Code verwenden, um sie abzufangen, damit der Benutzer nie darüber informiert wird), aber diese sind so konzipiert, dass sie zur Laufzeit auftreten, wenn der Joe-Benutzer die App verwendet.

Wenn Sie also glauben, dass Sie alle Ihre Fehler beseitigen können, verwenden Sie nur Ausnahmen. Wenn Sie denken, Sie können nicht ..... verwenden Sie Ausnahmen. Sie können weiterhin Debug-Asserts verwenden, um die Anzahl der Ausnahmen natürlich zu verringern.

Vergessen Sie nicht, dass viele der Voraussetzungen vom Benutzer bereitgestellte Daten sind. Sie benötigen daher eine gute Möglichkeit, den Benutzer darüber zu informieren, dass seine Daten nicht gut waren. Dazu müssen Sie häufig Fehlerdaten im Aufrufstapel an die Bits zurückgeben, mit denen er interagiert. Behauptungen sind dann nicht nützlich - doppelt, wenn Ihre App n-Tier ist.

Schließlich würde ich keine verwenden - Fehlercodes sind für Fehler, von denen Sie glauben, dass sie regelmäßig auftreten, weit überlegen. :) :)


0

Ich bevorzuge den zweiten. Während Ihre Tests möglicherweise gut gelaufen sind, sagt Murphy , dass etwas Unerwartetes schief gehen wird. Anstatt beim eigentlichen fehlerhaften Methodenaufruf eine Ausnahme zu erhalten, verfolgen Sie am Ende eine NullPointerException (oder eine gleichwertige), die 10 Stapelrahmen tiefer liegt.


0

Die vorherigen Antworten sind richtig: Verwenden Sie Ausnahmen für öffentliche API-Funktionen. Das einzige Mal, wenn Sie diese Regel biegen möchten, ist, wenn die Prüfung rechenintensiv ist. In diesem Fall Sie können es in einer Assertion setzen.

Wenn Sie der Meinung sind, dass eine Verletzung dieser Vorbedingung wahrscheinlich ist, behalten Sie sie als Ausnahme bei oder überarbeiten Sie die Vorbedingung.


0

Sie sollten beide verwenden. Behauptungen dienen Ihrer Bequemlichkeit als Entwickler. Ausnahmen erfassen Dinge, die Sie zur Laufzeit verpasst oder nicht erwartet haben.

Ich habe die Fehlerberichterstattungsfunktionen von glib geliebt, anstatt nur alte Behauptungen . Sie verhalten sich wie Assert-Anweisungen, aber anstatt das Programm anzuhalten, geben sie einfach einen Wert zurück und lassen das Programm fortfahren. Es funktioniert überraschend gut und als Bonus können Sie sehen, was mit dem Rest Ihres Programms passiert, wenn eine Funktion nicht "zurückgibt, was sie soll". Wenn es abstürzt, wissen Sie, dass Ihre Fehlerprüfung irgendwo anders auf der Straße nachlässig ist.

In meinem letzten Projekt habe ich diese Art von Funktionen verwendet, um die Überprüfung von Vorbedingungen zu implementieren. Wenn eine davon fehlschlug, druckte ich einen Stack-Trace in die Protokolldatei, lief aber weiter. Ersparte mir jede Menge Debugging-Zeit, wenn andere Leute beim Ausführen meines Debug-Builds auf ein Problem stießen.

#ifdef DEBUG
#define RETURN_IF_FAIL(expr)      do {                      \
 if (!(expr))                                           \
 {                                                      \
     fprintf(stderr,                                        \
        "file %s: line %d (%s): precondition `%s' failed.", \
        __FILE__,                                           \
        __LINE__,                                           \
        __PRETTY_FUNCTION__,                                \
        #expr);                                             \
     ::print_stack_trace(2);                                \
     return;                                                \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                   \
 {                                                              \
    fprintf(stderr,                                             \
        "file %s: line %d (%s): precondition `%s' failed.",     \
        __FILE__,                                               \
        __LINE__,                                               \
        __PRETTY_FUNCTION__,                                    \
        #expr);                                                 \
     ::print_stack_trace(2);                                    \
     return val;                                                \
 };               } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif

Wenn ich zur Laufzeit die Argumente überprüfen müsste, würde ich Folgendes tun:

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.
                                            // Goes away when debug off.

    if( ptr != NULL )
    {
       ...
    }

    return ptr;
}

Ich glaube nicht, dass ich in der OP-Frage etwas im Zusammenhang mit C ++ gesehen habe. Ich glaube, es sollte nicht in Ihrer Antwort enthalten sein.
ForceMagic

@ForceMagic: Die Frage hatte das C ++ - Tag im Jahr 2008, als ich diese Antwort veröffentlichte, und tatsächlich wurde das C ++ - Tag erst vor 5 Stunden entfernt. Unabhängig davon veranschaulicht der Code ein sprachunabhängiges Konzept.
Indiv

0

Ich habe versucht, einige der anderen Antworten hier mit meinen eigenen Ansichten zusammenzufassen.

Verwenden Sie Assertions für Fälle, in denen Sie sie in der Produktion deaktivieren möchten, um sie zu belassen. Der einzige wirkliche Grund für die Deaktivierung in der Produktion, jedoch nicht in der Entwicklung, besteht darin, das Programm zu beschleunigen. In den meisten Fällen ist diese Beschleunigung nicht signifikant, aber manchmal ist Code zeitkritisch oder der Test ist rechenintensiv. Wenn Code geschäftskritisch ist, sind Ausnahmen trotz der Verlangsamung möglicherweise am besten.

Wenn eine echte Chance auf Wiederherstellung besteht, verwenden Sie eine Ausnahme, da Zusicherungen nicht für die Wiederherstellung ausgelegt sind. Beispielsweise wird Code selten zur Wiederherstellung nach Programmierfehlern entwickelt, sondern zur Wiederherstellung nach Faktoren wie Netzwerkfehlern oder gesperrten Dateien. Fehler sollten nicht als Ausnahmen behandelt werden, nur weil sie außerhalb der Kontrolle des Programmierers liegen. Die Vorhersagbarkeit dieser Fehler im Vergleich zu Codierungsfehlern macht sie für die Wiederherstellung freundlicher.

Zu dem Argument, dass das Debuggen von Zusicherungen einfacher ist: Der Stack-Trace einer ordnungsgemäß benannten Ausnahme ist so einfach zu lesen wie eine Zusicherung. Guter Code sollte nur bestimmte Arten von Ausnahmen abfangen, damit Ausnahmen nicht unbemerkt bleiben, weil sie abgefangen werden. Ich denke jedoch, dass Java Sie manchmal zwingt, alle Ausnahmen abzufangen.


0

Als Faustregel gilt für mich, dass Sie Assert-Ausdrücke verwenden, um interne Fehler und Ausnahmen für externe Fehler zu finden. Von der folgenden Diskussion von Greg können Sie hier viel profitieren .

Assert-Ausdrücke werden verwendet, um Programmierfehler zu finden: entweder Fehler in der Programmlogik selbst oder Fehler in der entsprechenden Implementierung. Eine Assert-Bedingung überprüft, ob das Programm in einem definierten Zustand bleibt. Ein "definierter Zustand" ist im Grunde ein Zustand, der mit den Annahmen des Programms übereinstimmt. Beachten Sie, dass ein "definierter Zustand" für ein Programm kein "idealer Zustand" oder sogar "ein gewöhnlicher Zustand" oder sogar ein "nützlicher Zustand" sein muss, sondern später mehr zu diesem wichtigen Punkt.

Um zu verstehen, wie Zusicherungen in ein Programm passen, betrachten Sie eine Routine in einem C ++ - Programm, die einen Zeiger dereferenzieren soll. Sollte die Routine nun testen, ob der Zeiger vor der Dereferenzierung NULL ist, oder sollte sie behaupten, dass der Zeiger nicht NULL ist, und dann fortfahren und ihn unabhängig davon dereferenzieren?

Ich stelle mir vor, dass die meisten Entwickler beides tun möchten, die Zusicherung hinzufügen, aber auch den Zeiger auf einen NULL-Wert prüfen möchten, um nicht abzustürzen, falls die versicherte Bedingung fehlschlägt. An der Oberfläche scheint es die klügste Entscheidung zu sein, sowohl den Test als auch die Prüfung durchzuführen

Im Gegensatz zu den behaupteten Bedingungen bezieht sich die Fehlerbehandlung (Ausnahmen) eines Programms nicht auf Fehler im Programm, sondern auf Eingaben, die das Programm aus seiner Umgebung erhält. Dies sind häufig "Fehler" von Seiten einer Person, z. B. ein Benutzer, der versucht, sich bei einem Konto anzumelden, ohne ein Kennwort einzugeben. Und obwohl der Fehler einen erfolgreichen Abschluss der Programmaufgabe verhindern kann, liegt kein Programmfehler vor. Das Programm kann den Benutzer aufgrund eines externen Fehlers nicht ohne Kennwort anmelden - ein Fehler des Benutzers. Wenn die Umstände anders waren und der Benutzer das richtige Passwort eingegeben hat und das Programm es nicht erkannt hat; dann, obwohl das Ergebnis immer noch das gleiche wäre, würde der Fehler jetzt zum Programm gehören.

Die Fehlerbehandlung (Ausnahmen) dient zwei Zwecken. Die erste besteht darin, dem Benutzer (oder einem anderen Client) mitzuteilen, dass ein Fehler in der Programmeingabe erkannt wurde und was dies bedeutet. Das zweite Ziel besteht darin, die Anwendung nach Erkennung des Fehlers in einem genau definierten Zustand wiederherzustellen. Beachten Sie, dass das Programm selbst in dieser Situation nicht fehlerhaft ist. Zugegeben, das Programm befindet sich möglicherweise in einem nicht idealen Zustand oder sogar in einem Zustand, in dem nichts Nützliches getan werden kann, aber es liegt kein Programmierfehler vor. Im Gegenteil, da der Fehlerbehebungsstatus vom Programmdesign vorweggenommen wird, gibt er einen aus, den das Programm verarbeiten kann.

PS: Vielleicht möchten Sie die ähnliche Frage prüfen: Ausnahme gegen Behauptung .


-1

Siehe auch diese Frage :

In einigen Fällen sind Asserts beim Erstellen für die Freigabe deaktiviert. Möglicherweise haben Sie keine Kontrolle darüber (andernfalls könnten Sie mit Asserts darauf aufbauen), daher ist es möglicherweise eine gute Idee, dies so zu tun.

Das Problem beim "Korrigieren" der Eingabewerte besteht darin, dass der Aufrufer nicht das bekommt, was er erwartet, und dies kann zu Problemen oder sogar Abstürzen in ganz anderen Teilen des Programms führen, was das Debuggen zu einem Albtraum macht.

Normalerweise wirf ich eine Ausnahme in die if-Anweisung, um die Rolle der Behauptung zu übernehmen, falls sie deaktiviert sind

assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff
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.