Die Go
Sprachschöpfer schreiben :
Go liefert keine Aussagen. Sie sind zweifellos praktisch, aber wir haben die Erfahrung gemacht, dass Programmierer sie als Krücke verwenden, um nicht über eine ordnungsgemäße Fehlerbehandlung und Berichterstattung nachzudenken. Eine ordnungsgemäße Fehlerbehandlung bedeutet, dass Server nach nicht schwerwiegenden Fehlern weiterarbeiten, anstatt abzustürzen. Eine ordnungsgemäße Fehlerberichterstattung bedeutet, dass die Fehler direkt und auf den Punkt gebracht werden, sodass der Programmierer keine großen Absturzabläufe interpretieren kann. Genaue Fehler sind besonders wichtig, wenn der Programmierer, der die Fehler sieht, mit dem Code nicht vertraut ist.
Was ist Ihre Meinung dazu?
reflect.DeepEqual
, brauchen Sie es sicherlich nicht . Es ist praktisch, aber auf Kosten der Leistung (Unit-Tests sind ein guter Anwendungsfall). Andernfalls können Sie die für Ihre "Sammlung" geeignete Gleichheitsprüfung problemlos implementieren.for
Schleife in Go vergleichen (genau wie C). Es wäre wirklich schön, generische Slice-Operationen zu haben, obwohl der Vergleich kompliziert wird, wenn Zeiger und Strukturen beteiligt sind.Antworten:
Nein, es ist nichts falsch daran,
assert
solange Sie es wie vorgesehen verwenden.Das heißt, es soll dazu dienen, Fälle abzufangen, die beim Debuggen "nicht passieren können", im Gegensatz zur normalen Fehlerbehandlung.
quelle
Nein, weder
goto
nochassert
sind böse. Aber beide können missbraucht werden.Assert ist für die Überprüfung der geistigen Gesundheit. Dinge, die das Programm beenden sollten, wenn sie nicht korrekt sind. Nicht zur Validierung oder als Ersatz für die Fehlerbehandlung.
quelle
goto
Bedacht umgeht?goto
aus rein religiösen Gründen vermeiden möchten, und verwenden Sie dann einfach,goto
anstatt zu verschleiern, was Sie tun. Mit anderen Worten: Wenn Sie beweisen können, dass Sie wirklich brauchengoto
, und die einzige Alternative darin besteht, eine Menge sinnloser Gerüste zu bauen, die letztendlich dasselbe tun, ohne jedoch die Goto-Polizei zu verändern ... dann verwenden Sie einfachgoto
. Voraussetzung dafür ist natürlich das Bit "Wenn Sie beweisen können, dass Sie es wirklich brauchengoto
". Oft tun es die Leute nicht. Das bedeutet immer noch nicht, dass es von Natur aus eine schlechte Sache ist.goto
wird im Linux-Kernel für die Code-Bereinigung verwendetNach dieser Logik sind Haltepunkte auch böse.
Asserts sollten als Debugging-Hilfe verwendet werden und sonst nichts. "Böse" ist, wenn Sie versuchen, sie anstelle der Fehlerbehandlung zu verwenden.
Asserts sollen Ihnen als Programmierer helfen, Probleme zu erkennen und zu beheben, die nicht existieren dürfen, und überprüfen, ob Ihre Annahmen zutreffen.
Sie haben nichts mit Fehlerbehandlung zu tun, aber leider missbrauchen einige Programmierer sie als solche und erklären sie dann für "böse".
quelle
Ich benutze gerne behaupten viel. Ich finde es sehr nützlich, wenn ich zum ersten Mal Anwendungen erstelle (möglicherweise für eine neue Domain). Anstatt eine sehr ausgefallene Fehlerprüfung durchzuführen (die ich als vorzeitige Optimierung betrachten würde), codiere ich schnell und füge viele Asserts hinzu. Nachdem ich mehr über die Funktionsweise der Dinge weiß, schreibe ich einige der Zusicherungen neu, entferne sie und ändere sie zur besseren Fehlerbehandlung.
Aufgrund von Behauptungen verbringe ich viel weniger Zeit mit dem Codieren / Debuggen von Programmen.
Mir ist auch aufgefallen, dass mir die Behauptungen helfen, über viele Dinge nachzudenken, die meine Programme beschädigen könnten.
quelle
Als zusätzliche Information bietet go eine integrierte Funktion
panic
. Dies kann anstelle von verwendet werdenassert
. Z.Bpanic
druckt den Stack-Trace, hat also in gewisser Weise den Zweck vonassert
.quelle
Sie sollten zur Erkennung von Fehlern im Programm verwendet werden. Keine schlechte Benutzereingabe.
Bei richtiger Anwendung sind sie nicht böse.
quelle
Dies kommt häufig vor, und ich denke, ein Problem, das die Verteidigung von Behauptungen verwirrend macht, besteht darin, dass sie häufig auf der Überprüfung von Argumenten beruhen. Betrachten Sie also dieses andere Beispiel, wann Sie eine Behauptung verwenden könnten:
Sie verwenden eine Ausnahme für die Eingabe, da Sie erwarten, dass Sie manchmal schlechte Eingaben erhalten. Sie behaupten, dass die Liste sortiert ist, um Ihnen zu helfen, einen Fehler in Ihrem Algorithmus zu finden, den Sie per Definition nicht erwarten. Die Behauptung befindet sich nur im Debug-Build. Obwohl die Prüfung teuer ist, macht es Ihnen nichts aus, sie bei jedem einzelnen Aufruf der Routine auszuführen.
Sie müssen Ihren Produktionscode noch einem Unit-Test unterziehen, aber dies ist eine andere und ergänzende Methode, um sicherzustellen, dass Ihr Code korrekt ist. Unit-Tests stellen sicher, dass Ihre Routine der Benutzeroberfläche gerecht wird, während Assertions eine genauere Methode darstellen, um sicherzustellen, dass Ihre Implementierung genau das tut, was Sie von ihr erwarten.
quelle
Behauptungen sind nicht böse, können aber leicht missbraucht werden. Ich stimme der Aussage zu, dass "Behauptungen häufig als Krücke verwendet werden, um nicht über eine ordnungsgemäße Fehlerbehandlung und Berichterstattung nachzudenken". Ich habe das ziemlich oft gesehen.
Persönlich verwende ich gerne Behauptungen, weil sie Annahmen dokumentieren, die ich möglicherweise beim Schreiben meines Codes gemacht habe. Wenn diese Annahmen während der Pflege des Codes verletzt werden, kann das Problem während des Tests erkannt werden. Ich lege jedoch Wert darauf, jede Behauptung aus meinem Code zu entfernen, wenn ich einen Produktionsbuild durchführe (dh mit #ifdefs). Indem ich die Behauptungen im Produktionsaufbau streife, eliminiere ich das Risiko, dass jemand sie als Krücke missbraucht.
Es gibt auch ein anderes Problem mit Behauptungen. Zusicherungen werden nur zur Laufzeit überprüft. Es ist jedoch häufig der Fall, dass die Prüfung, die Sie durchführen möchten, zur Kompilierungszeit durchgeführt wurde. Es ist vorzuziehen, ein Problem beim Kompilieren zu erkennen. Für C ++ - Programmierer bietet boost BOOST_STATIC_ASSERT, mit dem Sie dies tun können. Für C-Programmierer beschreibt dieser Artikel ( Linktext ) eine Technik, mit der Assertions zur Kompilierungszeit ausgeführt werden können.
Zusammenfassend lautet die Faustregel, die ich befolge: Verwenden Sie keine Zusicherungen in einem Produktionsbuild und verwenden Sie nach Möglichkeit nur Zusicherungen für Dinge, die zur Kompilierungszeit nicht überprüft werden können (dh zur Laufzeit überprüft werden müssen).
quelle
Ich gebe zu, Asserts verwendet zu haben, ohne die ordnungsgemäße Fehlerberichterstattung in Betracht zu ziehen. Dies bedeutet jedoch nicht, dass sie bei korrekter Verwendung sehr hilfreich sind.
Sie sind besonders nützlich, wenn Sie dem "Crash Early" -Prinzip folgen möchten. Angenommen, Sie implementieren einen Referenzzählmechanismus. An bestimmten Stellen in Ihrem Code wissen Sie, dass die Nachzählung null oder eins sein sollte. Nehmen wir außerdem an, dass das Programm nicht sofort abstürzt, wenn der Refcount falsch ist. In der nächsten Nachrichtenschleife ist es jedoch schwierig herauszufinden, warum ein Fehler aufgetreten ist. Eine Behauptung wäre hilfreich gewesen, um den Fehler näher an seinem Ursprung zu erkennen.
quelle
Ich bevorzuge es, Code zu vermeiden, der beim Debuggen und Freigeben verschiedene Aufgaben ausführt.
Es ist jedoch nützlich, den Debugger unter einer Bedingung einzuschalten und alle Datei- / Zeileninformationen zu haben, auch den genauen Ausdruck und den genauen Wert.
Eine Behauptung, die "den Zustand nur beim Debuggen bewerten würde", kann eine Leistungsoptimierung sein und ist daher nur in 0,0001% der Programme nützlich - wo die Leute wissen, was sie tun. In allen anderen Fällen ist dies schädlich, da der Ausdruck den Programmstatus tatsächlich ändern kann:
assert(2 == ShroedingersCat.GetNumEars());
würde das Programm dazu bringen, verschiedene Dinge beim Debuggen und Freigeben zu tun.Wir haben eine Reihe von Assert-Makros entwickelt, die eine Ausnahme auslösen und dies sowohl in der Debug- als auch in der Release-Version tun. Zum Beispiel
THROW_UNLESS_EQ(a, 20);
würde eine Ausnahme mit der Nachricht what () ausgelöst, die sowohl Datei-, Zeilen- als auch die tatsächlichen Werte von a usw. enthält. Nur ein Makro hätte die Macht dafür. Der Debugger kann so konfiguriert sein, dass er beim "Auslösen" des spezifischen Ausnahmetyps unterbrochen wird.quelle
Ich mag Behauptungen nicht sehr. Ich würde nicht so weit gehen zu sagen, dass sie böse sind.
Grundsätzlich wird eine Zusicherung dasselbe tun wie eine ungeprüfte Ausnahme. Die einzige Ausnahme besteht darin, dass die Zusicherung (normalerweise) nicht für das Endprodukt aufbewahrt werden sollte.
Wenn Sie beim Debuggen und Erstellen des Systems ein Sicherheitsnetz für sich selbst erstellen, warum sollten Sie dieses Sicherheitsnetz Ihrem Kunden, Ihrem Support-Helpdesk oder jedem, der die von Ihnen derzeit erstellte Software verwenden kann, verweigern? Verwenden Sie Ausnahmen ausschließlich für Behauptungen und Ausnahmesituationen. Durch das Erstellen einer geeigneten Ausnahmehierarchie können Sie sehr schnell voneinander unterscheiden. Außer diesmal bleibt die Zusicherung bestehen und kann im Falle eines Fehlers, der sonst verloren gehen würde, wertvolle Informationen liefern.
Daher verstehe ich die Entwickler von Go vollständig, indem ich Asserts vollständig entferne und Programmierer dazu zwinge, Ausnahmen zu verwenden, um mit der Situation umzugehen. Es gibt eine einfache Erklärung dafür, Ausnahmen sind nur ein besserer Mechanismus für den Job, warum bei den archaischen Behauptungen bleiben?
quelle
Kurze Antwort: Nein, ich glaube, Behauptungen können nützlich sein
quelle
Ich habe kürzlich damit begonnen, meinem Code einige Asserts hinzuzufügen, und so habe ich es gemacht:
Ich teile meinen Code mental in Grenzcode und internen Code auf. Grenzcode ist Code, der Benutzereingaben verarbeitet, Dateien liest und Daten aus dem Netzwerk abruft. In diesem Code fordere ich eine Eingabe in einer Schleife an, die nur beendet wird, wenn die Eingabe gültig ist (bei interaktiver Benutzereingabe), oder bei nicht wiederherstellbaren beschädigten Datei- / Netzwerkdaten Ausnahmen auszulösen.
Interner Code ist alles andere. Beispielsweise kann eine Funktion, die eine Variable in meiner Klasse festlegt, als definiert werden
und eine Funktion, die Eingaben von einem Netzwerk erhält, könnte als solche lauten:
Dies gibt mir zwei Ebenen von Schecks. Alles, wo die Daten zur Laufzeit ermittelt werden, erhält immer eine Ausnahme oder sofortige Fehlerbehandlung. Dieses zusätzliche Einchecken
Class::f
mit derassert
Anweisung bedeutet jedoch, dassClass::f
ich immer noch eine Überprüfung der geistigen Gesundheit habe , wenn jemals ein interner Code aufgerufen wird. Mein interner Code übergibt möglicherweise kein gültiges Argument (da ich möglicherweisevalue
aus einer komplexen Reihe von Funktionen berechnet habe ). Daher möchte ich, dass die Behauptung in der Einstellungsfunktion dokumentiert, dass unabhängig davon, wer die Funktion aufruft,value
nicht größer als oder sein darf gleichend
.Dies scheint in das zu passen, was ich an einigen Stellen lese, dass Behauptungen in einem gut funktionierenden Programm unmöglich zu verletzen sein sollten, während Ausnahmen für außergewöhnliche und fehlerhafte Fälle gelten sollten, die noch möglich sind. Da ich theoretisch alle Eingaben validiere, sollte es nicht möglich sein, dass meine Behauptung ausgelöst wird. Wenn ja, ist mein Programm falsch.
quelle
Wenn die Behauptungen, von denen Sie sprechen, bedeuten, dass sich das Programm erbricht und dann existiert, können Behauptungen sehr schlecht sein. Das heißt nicht, dass sie es immer sind falsch verwendet werden, sondern ein Konstrukt, das sehr leicht missbraucht werden kann. Sie haben auch viele bessere Alternativen. Solche Dinge sind gute Kandidaten, um als böse bezeichnet zu werden.
Beispielsweise sollte ein Modul eines Drittanbieters (oder wirklich ein beliebiges Modul) das aufrufende Programm fast nie verlassen. Dies gibt dem aufrufenden Programmierer keine Kontrolle darüber, welches Risiko das Programm in diesem Moment eingehen sollte. In vielen Fällen sind Daten so wichtig, dass selbst das Speichern beschädigter Daten besser ist als das Verlieren dieser Daten. Asserts können Sie zwingen, Daten zu verlieren.
Einige Alternativen zu Behauptungen:
Einige Referenzen:
Sogar Leute, die sich für die Behauptung einsetzen, denken, sie sollten nur in der Entwicklung und nicht in der Produktion verwendet werden:
Diese Person sagt, dass Asserts verwendet werden sollten, wenn das Modul möglicherweise Daten beschädigt hat, die nach dem Auslösen einer Ausnahme bestehen bleiben: http://www.advogato.org/article/949.html . Dies ist sicherlich ein vernünftiger Punkt. Ein externes Modul sollte jedoch niemals vorschreiben, wie wichtig beschädigte Daten für das aufrufende Programm sind (indem es "für" sie beendet). Der richtige Weg, dies zu handhaben, besteht darin, eine Ausnahme auszulösen, die deutlich macht, dass sich das Programm jetzt möglicherweise in einem inkonsistenten Zustand befindet. Und da gute Programme meistens aus Modulen bestehen (mit ein wenig Klebercode in der ausführbaren Hauptdatei), sind Asserts fast immer das Falsche.
quelle
assert
ist sehr nützlich und kann Ihnen viel Backtracking ersparen, wenn unerwartete Fehler auftreten, indem Sie das Programm bei den ersten Anzeichen von Problemen anhalten.Andererseits ist es sehr leicht zu missbrauchen
assert
.Die richtige, korrekte Version wäre ungefähr so:
Also ... auf lange Sicht ... im Großen und Ganzen ... muss ich zustimmen, dass
assert
das missbraucht werden kann. Das mache ich die ganze Zeit.quelle
assert
wird zur Fehlerbehandlung missbraucht, weil weniger getippt wird.Als Sprachdesigner sollten sie daher eher darauf achten, dass eine ordnungsgemäße Fehlerbehandlung mit noch geringerer Eingabe möglich ist. Das Ausschließen von Assert, weil Ihr Ausnahmemechanismus ausführlich ist, ist nicht die Lösung. Oh warte, Go hat auch keine Ausnahmen. Schade :)
quelle
Ich wollte dem Autor in den Kopf treten, als ich das sah.
Ich verwende Asserts die ganze Zeit im Code und ersetze sie schließlich alle, wenn ich mehr Code schreibe. Ich verwende sie, wenn ich die erforderliche Logik nicht geschrieben habe und benachrichtigt werden möchte, wenn ich auf den Code stoße, anstatt eine Ausnahme zu schreiben, die gelöscht wird, wenn sich das Projekt dem Abschluss nähert.
Ausnahmen fügen sich auch leichter in den Produktionscode ein, was mir nicht gefällt. Eine Behauptung ist leichter zu bemerken als
throw new Exception("Some generic msg or 'pretend i am an assert'");
quelle
Mein Problem mit diesen Antworten zur Verteidigung der Behauptung ist, dass niemand klar spezifiziert, was sie von einem normalen schwerwiegenden Fehler unterscheidet und warum eine Behauptung keine Teilmenge einer Ausnahme sein kann . Was ist nun, wenn die Ausnahme nie abgefangen wird? Ist das eine Behauptung der Nomenklatur? Und warum sollten Sie jemals eine Einschränkung in der Sprache auferlegen wollen, dass eine Ausnahme ausgelöst werden kann, die / nichts / behandeln kann?
quelle
func()
mit einer negativen Nummer an. Jetzt, plötzlich mit Ihren Behauptungen, haben Sie den Teppich unter mir herausgezogen und mir keine Chance gegeben, mich zu erholen, anstatt mir höflich zu sagen, was ich verlange, kann nicht getan werden. Es ist nichts Falsches daran, das Programm zu beschuldigen, sich schlecht benommen zu haben und es zu zitieren: Das Problem ist, dass Sie den Akt der Durchsetzung eines Gesetzes verwischen und den Verbrecher verurteilen.Ja, Behauptungen sind böse.
Oft werden sie an Orten eingesetzt, an denen eine ordnungsgemäße Fehlerbehandlung angewendet werden sollte. Gewöhnen Sie sich von Anfang an daran, die richtige Fehlerbehandlung in der Produktionsqualität zu schreiben!
Normalerweise behindern sie das Schreiben von Komponententests (es sei denn, Sie schreiben eine benutzerdefinierte Zusicherung, die mit Ihrem Testgeschirr interagiert). Dies liegt häufig daran, dass sie dort eingesetzt werden, wo eine ordnungsgemäße Fehlerbehandlung angewendet werden sollte.
Meistens werden sie aus Release-Builds kompiliert, was bedeutet, dass keiner ihrer "Tests" verfügbar ist, wenn Sie den Code ausführen, den Sie tatsächlich veröffentlichen. Angesichts der Tatsache, dass in Multithread-Situationen die schlimmsten Probleme häufig nur im Release-Code auftreten, kann dies schlimm sein.
Manchmal sind sie eine Krücke für ansonsten kaputte Designs; Das heißt, das Design des Codes ermöglicht es einem Benutzer, ihn so aufzurufen, dass er nicht aufgerufen werden sollte, und die Behauptung "verhindert" dies. Korrigieren Sie das Design!
Ich habe 2005 in meinem Blog darüber mehr geschrieben: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html
quelle
Nicht so sehr böse als generell kontraproduktiv. Es gibt eine Trennung zwischen permanenter Fehlerprüfung und Debugging. Assert lässt die Leute denken, dass jedes Debuggen dauerhaft sein sollte und verursacht massive Lesbarkeitsprobleme, wenn es häufig verwendet wird. Die permanente Fehlerbehandlung sollte besser sein als bei Bedarf, und da die Behauptung eigene Fehler verursacht, ist dies eine ziemlich fragwürdige Praxis.
quelle
Ich benutze niemals assert (), Beispiele zeigen normalerweise so etwas:
Das ist schlecht, ich mache das nie. Was ist, wenn mein Spiel ein paar Monster zuweist? Warum sollte ich das Spiel zum Absturz bringen? Stattdessen sollten Sie die Fehler ordnungsgemäß behandeln. Tun Sie also etwas wie:
quelle
new
kehrt nie zurücknullptr
, es wirft.