Ich habe viele Artikel (und einige ähnliche Fragen, die auf StackOverflow veröffentlicht wurden) darüber gelesen, wie und wann Behauptungen verwendet werden sollen, und ich habe sie gut verstanden. Trotzdem verstehe ich nicht, welche Art von Motivation mich antreiben sollte, Debug.Assert
anstatt eine einfache Ausnahme zu machen. Was ich damit meine, ist, dass in .NET die Standardantwort auf eine fehlgeschlagene Behauptung darin besteht, "die Welt anzuhalten" und dem Benutzer ein Meldungsfeld anzuzeigen. Obwohl diese Art von Verhalten geändert werden könnte, finde ich es sehr ärgerlich und überflüssig, dies zu tun, während ich stattdessen einfach eine geeignete Ausnahme auslösen könnte. Auf diese Weise könnte ich den Fehler leicht in das Protokoll der Anwendung schreiben, bevor ich die Ausnahme auslöse, und außerdem friert meine Anwendung nicht unbedingt ein.
Warum sollte ich also, wenn überhaupt, Debug.Assert
anstelle einer einfachen Ausnahme verwenden? Wenn Sie eine Behauptung dort platzieren, wo sie nicht sein sollte, kann dies zu allen Arten von "unerwünschtem Verhalten" führen. Aus meiner Sicht erhalte ich also wirklich nichts, wenn ich eine Behauptung verwende, anstatt eine Ausnahme auszulösen. Stimmen Sie mir zu oder fehlt mir hier etwas?
Hinweis: Ich verstehe voll und ganz, was der Unterschied "in der Theorie" ist (Debug vs. Release, Verwendungsmuster usw.), aber aus meiner Sicht ist es besser, eine Ausnahme auszulösen, als eine Bestätigung durchzuführen. Wenn ein Fehler in einer Produktionsversion entdeckt wird, möchte ich trotzdem, dass die "Behauptung" fehlschlägt (schließlich ist der "Overhead" lächerlich gering), daher ist es besser, stattdessen eine Ausnahme auszulösen.
Bearbeiten: So wie ich es sehe, bedeutet eine fehlgeschlagene Bestätigung, dass die Anwendung in einen beschädigten, unerwarteten Zustand übergegangen ist. Warum sollte ich die Ausführung fortsetzen wollen? Es spielt keine Rolle, ob die Anwendung auf einer Debug- oder Release-Version ausgeführt wird. Das gleiche gilt für beide
quelle
Antworten:
Obwohl ich der Meinung bin, dass Ihre Argumentation plausibel ist - das heißt, wenn eine Behauptung unerwartet verletzt wird, ist es sinnvoll, die Ausführung durch Werfen zu stoppen -, würde ich persönlich keine Ausnahmen anstelle von Behauptungen verwenden. Hier ist der Grund:
Wie andere gesagt haben, sollten Behauptungen Situationen dokumentieren , die unmöglich sind , so dass der Entwickler informiert wird, wenn die angeblich unmögliche Situation eintritt. Ausnahmen bieten dagegen einen Kontrollflussmechanismus für außergewöhnliche, unwahrscheinliche oder fehlerhafte Situationen, aber nicht für unmögliche Situationen. Für mich ist der Hauptunterschied folgender:
Es sollte IMMER möglich sein, einen Testfall zu erstellen, der eine bestimmte Wurfanweisung ausführt. Wenn es nicht möglich ist, einen solchen Testfall zu erstellen, haben Sie einen Codepfad in Ihrem Programm, der niemals ausgeführt wird, und dieser sollte als toter Code entfernt werden.
Es sollte NIEMALS möglich sein, einen Testfall zu erstellen, bei dem eine Behauptung ausgelöst wird. Wenn eine Zusicherung ausgelöst wird, ist entweder der Code falsch oder die Zusicherung ist falsch. In jedem Fall muss sich etwas im Code ändern.
Deshalb würde ich eine Behauptung nicht durch eine Ausnahme ersetzen. Wenn die Zusicherung nicht tatsächlich ausgelöst werden kann, bedeutet das Ersetzen durch eine Ausnahme, dass Sie einen nicht testbaren Codepfad in Ihrem Programm haben . Ich mag nicht testbare Codepfade nicht.
quelle
Behauptungen werden verwendet, um das Verständnis des Programmierers für die Welt zu überprüfen. Eine Zusicherung sollte nur fehlschlagen, wenn der Programmierer etwas falsch gemacht hat. Verwenden Sie beispielsweise niemals eine Zusicherung, um Benutzereingaben zu überprüfen.
Aktiviert den Test für Bedingungen, die "nicht auftreten können". Ausnahmen sind Bedingungen, die "nicht passieren sollten, aber tun".
Behauptungen sind nützlich, da Sie zur Erstellungszeit (oder sogar zur Laufzeit) ihr Verhalten ändern können. Beispielsweise werden in Release-Builds die Asserts häufig nicht einmal überprüft, da sie unnötigen Overhead verursachen. Dies ist auch etwas zu beachten: Ihre Tests werden möglicherweise nicht einmal ausgeführt.
Wenn Sie Ausnahmen anstelle von Asserts verwenden, verlieren Sie etwas an Wert:
Der Code ist ausführlicher, da das Testen und Auslösen einer Ausnahme mindestens zwei Zeilen umfasst, während eine Zusicherung nur eine ist.
Ihr Test- und Wurfcode wird immer ausgeführt, während Asserts kompiliert werden können.
Sie verlieren die Kommunikation mit anderen Entwicklern, da Asserts eine andere Bedeutung haben als der Produktcode, der überprüft und ausgelöst wird. Wenn Sie eine Programmierzusicherung wirklich testen, verwenden Sie eine Bestätigung.
Mehr hier: http://nedbatchelder.com/text/assert.html
quelle
BEARBEITEN: Als Antwort auf die Bearbeitung / Notiz, die Sie in Ihrem Beitrag vorgenommen haben: Es klingt so, als ob die Verwendung von Ausnahmen das Richtige ist, anstatt Behauptungen für die Art von Dingen zu verwenden, die Sie ausführen möchten. Ich denke, der mentale Stolperstein, auf den Sie stoßen, besteht darin, dass Sie Ausnahmen und Behauptungen in Betracht ziehen, um denselben Zweck zu erfüllen, und Sie versuchen herauszufinden, welche davon „richtig“ wäre. Es gibt zwar einige Überschneidungen bei der Verwendung von Behauptungen und Ausnahmen, aber verwechseln Sie nicht, dass sie unterschiedliche Lösungen für dasselbe Problem darstellen - sie sind es nicht. Behauptungen und Ausnahmen haben jeweils ihren eigenen Zweck, ihre eigenen Stärken und Schwächen.
Ich wollte eine Antwort in meinen eigenen Worten schreiben, aber dies wird dem Konzept besser gerecht als ich es hätte:
C # Station: Behauptungen
Verwenden Sie grundsätzlich Ausnahmen für Dinge, die in einer Produktionsanwendung abgefangen / behandelt werden müssen, und verwenden Sie Assertions, um logische Überprüfungen durchzuführen, die für die Entwicklung nützlich sind, aber in der Produktion deaktiviert sind.
quelle
Ich denke, ein (erfundenes) praktisches Beispiel kann helfen, den Unterschied zu beleuchten:
(angepasst aus der Batch-Erweiterung von MoreLinq )
Wie Eric Lippert et al. Gesagt haben, behaupten Sie nur Dinge, von denen Sie erwarten, dass sie korrekt sind, nur für den Fall, dass Sie (der Entwickler) sie versehentlich an einer anderen Stelle falsch verwendet haben , damit Sie Ihren Code reparieren können. Sie lösen grundsätzlich Ausnahmen aus, wenn Sie keine Kontrolle darüber haben oder nicht vorhersehen können, was hereinkommt, z. B. für Benutzereingaben , damit die fehlerhaften Daten angemessen reagieren können (z. B. der Benutzer).
quelle
Ein weiteres Nugget von Code Complete :
Er fügt einige Richtlinien hinzu, was behauptet werden soll und was nicht.
Ausnahmen dagegen:
Wenn Sie dieses Buch nicht haben, sollten Sie es kaufen.
quelle
Debug.Assert funktioniert standardmäßig nur in Debug-Builds. Wenn Sie also in Ihren Release-Builds ein unerwartetes Verhalten feststellen möchten, müssen Sie Ausnahmen verwenden oder die Debug-Konstante in Ihren Projekteigenschaften aktivieren (was in berücksichtigt wird) allgemein keine gute Idee zu sein).
quelle
Verwenden Behauptungen für Dinge , die ARE möglich, sollte aber nicht passieren (wenn es nicht möglich wäre, warum würden Sie eine Behauptung sagen?).
Klingt das nicht nach einem Fall, in dem man einen verwendet
Exception
? Warum würden Sie eine Behauptung anstelle einer verwendenException
?Weil es Code geben sollte, der vor Ihrer Zusicherung aufgerufen wird, der verhindern würde, dass der Parameter der Zusicherung falsch ist.
Normalerweise gibt es keinen Code vor Ihnen
Exception
, der garantiert, dass er nicht geworfen wird.Warum ist es gut, dass
Debug.Assert()
in prod zusammengestellt wird? Wenn Sie es im Debug wissen möchten, möchten Sie es nicht im Produkt wissen?Sie möchten es nur während der Entwicklung, denn sobald Sie
Debug.Assert(false)
Situationen gefunden haben, schreiben Sie Code, um sicherzustellen, dassDebug.Assert(false)
dies nicht erneut geschieht. Sobald die Entwicklung abgeschlossen ist, wird davon ausgegangen, dass Sie dieDebug.Assert(false)
Situationen gefunden und behoben habenDebug.Assert()
können Sie sie sicher kompilieren, da sie jetzt redundant sind.quelle
Angenommen, Sie sind Mitglied eines ziemlich großen Teams und es gibt mehrere Personen, die alle an derselben allgemeinen Codebasis arbeiten, einschließlich der Überlappung von Klassen. Sie können eine Methode erstellen, die von mehreren anderen Methoden aufgerufen wird. Um Sperrenkonflikte zu vermeiden, fügen Sie ihr keine separate Sperre hinzu, sondern "nehmen an", dass sie zuvor von der aufrufenden Methode mit einer bestimmten Sperre gesperrt wurde. Zum Beispiel Debug.Assert (RepositoryLock.IsReadLockHeld || RepositoryLock.IsWriteLockHeld); Die anderen Entwickler übersehen möglicherweise einen Kommentar, der besagt, dass die aufrufende Methode die Sperre verwenden muss, können dies jedoch nicht ignorieren.
quelle