Ich habe gerade einen schönen Code in unserer Unternehmens-App entdeckt, der Try-Catch-Blöcke als logische Operatoren verwendet.
Das heißt, "mache etwas Code, wenn das diesen Fehler auslöst, mache diesen Code, aber wenn das diesen Fehler auslöst, mache stattdessen diese dritte Sache".
Es verwendet "finally" als "else" -Anweisung.
Ich weiß, dass dies von Natur aus falsch ist, aber bevor ich mich für einen Kampf entschied, hoffte ich auf einige gut durchdachte Argumente.
Und hey, wenn Sie Argumente für die Verwendung von Try-Catch auf diese Weise haben, sagen Sie es bitte.
Für alle, die sich fragen, ist die Sprache C # und der fragliche Code besteht aus mehr als 30 Zeilen und sucht nach bestimmten Ausnahmen. Es werden nicht ALLE Ausnahmen behandelt.
quelle
try
. Nicht jeder Ausnahmefall, der eine Ausnahme im Allgemeinen rechtfertigt, muss in diesem speziellen Fall tödlich sein. Könnten Sie es also einfacher, gleichermaßen oder robuster machen, ohne Ausnahmen zu verwenden?Antworten:
Die Ausnahmebehandlung ist in der Regel eine teure Methode zur Flusskontrolle (sicherlich für C # und Java).
Die Laufzeitumgebung erledigt eine Menge Arbeit, wenn ein Ausnahmeobjekt erstellt wird - Zusammenstellen des Stack-Trace, Herausfinden, wo die Ausnahme behandelt wird und vieles mehr.
All dies kostet Arbeitsspeicher und CPU-Ressourcen, die nicht erweitert werden müssen, wenn Flusssteuerungsanweisungen zur Flusssteuerung verwendet werden.
Zusätzlich gibt es eine semantische Frage. Ausnahmen gelten für Ausnahmesituationen, nicht für die normale Flusskontrolle. Man sollte die Ausnahmebehandlung verwenden, um unvorhergesehene / außergewöhnliche Situationen zu behandeln, nicht wie im normalen Programmablauf, da sonst eine nicht erfasste Ausnahme viel weniger aussagt.
Abgesehen von diesen beiden geht es darum, dass andere den Code lesen. Die meisten Programmierer erwarten nicht, Ausnahmen auf diese Weise zu verwenden. Daher leidet die Lesbarkeit und das Verständnis Ihres Codes darunter. Wenn man "Exception" sieht, denkt man - etwas Schlimmes ist passiert, etwas, das normalerweise nicht passieren soll. Die Verwendung von Ausnahmen auf diese Weise ist also einfach verwirrend.
quelle
Wie kannst du das Wissen? Ich habe all dieses "Wissen" aufgegeben und glaube jetzt nur, dass der einfachste Code der beste ist. Angenommen, Sie möchten eine Zeichenfolge in eine optionale konvertieren, die leer ist, wenn die Analyse fehlschlägt. Es ist nichts falsch mit:
Ich stimme der üblichen Auslegung von "Ausnahmen gelten für außergewöhnliche Umstände" überhaupt nicht zu. Wenn eine Funktion keinen verwendbaren Wert zurückgeben kann oder eine Methode ihre Post-Bedingungen nicht erfüllen kann, lösen Sie eine Ausnahme aus. Es spielt keine Rolle, wie oft diese Ausnahmen ausgelöst werden, bis ein nachgewiesenes Leistungsproblem vorliegt.
Ausnahmen vereinfachen den Code, indem sie die Trennung der Fehlerbehandlung vom normalen Ablauf ermöglichen. Schreiben Sie einfach den einfachsten Code, und wenn es einfacher ist, try-catch zu verwenden oder eine Ausnahme auszulösen, dann tun Sie dies.
Ausnahmen vereinfachen das Testen, indem die Anzahl der Pfade durch den Code verringert wird. Eine Funktion ohne Verzweigungen führt entweder zum Abschluss oder löst eine Ausnahme aus. Eine Funktion mit mehreren
if
Anweisungen zum Überprüfen auf Fehlercodes verfügt über viele mögliche Pfade. Es ist sehr einfach, eine der Bedingungen falsch zu machen oder vollständig zu vergessen, so dass eine Fehlerbedingung ignoriert wird.quelle
int i; if(long.TryParse(s, out i)) return i; else return null;
. TryParse gibt false zurück, wenn der String nicht analysiert werden konnte. Wenn es analysiert werden könnte, setzt es das Ausgabeargument auf die Ergebnisse der Analyse und gibt true zurück. Es treten keine Ausnahmen auf (auch nicht intern in TryParse), und insbesondere keine Ausnahmen eines Typs, der Programmiererfehler bedeutet, wie sie in .NETsFormatException
immer angezeigt werden.Debug- und Wartungsarbeiten sind sehr schwierig, wenn der Steuerungsfluss mithilfe von Ausnahmen ausgeführt wird.
Inhärent sind Ausnahmen als Mechanismus zur Änderung des normalen Kontrollflusses Ihres Programms gedacht - zur Durchführung ungewöhnlicher Aktivitäten, die ungewöhnliche Nebenwirkungen hervorrufen, um aus einer besonders engen Bindung herauszukommen, die nicht mit weniger Kompliziertheit bewältigt werden kann meint. Ausnahmen sind außergewöhnlich. Dies bedeutet, dass abhängig von der Umgebung, in der Sie arbeiten, die Verwendung einer Ausnahme für den regulären Steuerungsfluss Folgendes verursachen kann:
Ineffizienz Die zusätzlichen Rahmen, durch die die Umgebung springen muss, um die relativ schwierigen Kontextänderungen, die für Ausnahmen erforderlich sind, sicher durchzuführen, erfordern Instrumente und Ressourcen.
Schwierigkeiten beim Debuggen Manchmal werden nützliche Informationen (beim Versuch, ein Programm zu debuggen) aus dem Fenster geworfen, wenn Ausnahmen auftreten. Sie können den Überblick über den Programmstatus oder den Verlauf verlieren, der für das Verständnis des Laufzeitverhaltens relevant ist
Wartungsprobleme Der Ausführungsfluss ist durch Ausnahmesprünge nur schwer zu verfolgen. Darüber hinaus wird möglicherweise eine Ausnahme aus dem Black-Box-Typcode ausgelöst, der sich beim Auslösen einer Ausnahme möglicherweise nicht so leicht verständlich verhält.
Schlechte Entwurfsentscheidungen Programme, die auf diese Weise erstellt wurden, fördern eine Geisteshaltung, die sich in den meisten Fällen nicht leicht auf die elegante Lösung von Problemen übertragen lässt. Die Komplexität des endgültigen Programms hält den Programmierer davon ab, dessen Ausführung vollständig zu verstehen, und ermutigt ihn, Entscheidungen zu treffen, die zu kurzfristigen Verbesserungen mit hohen langfristigen Kosten führen.
quelle
Ich habe dieses Muster schon mehrmals gesehen.
Es gibt zwei Hauptprobleme:
goto
. Sprünge gelten aus mehreren Gründen als schädlich. Sie sollten verwendet werden, wenn alle Alternativen erhebliche Nachteile haben. Tatsächlich erinnere ich mich in meinem gesamten Code nur an zwei Fälle, in denen Sprünge eindeutig die beste Lösung waren.quelle
Manchmal sind Ausnahmen am schnellsten. Ich habe Fälle gesehen, in denen Nullobjektausnahmen sogar in Java schneller waren als die Verwendung von Kontrollstrukturen (ich kann die Studie derzeit nicht zitieren, daher müssen Sie mir vertrauen). Das Problem tritt auf, wenn Java sich tatsächlich die Zeit nehmen und den Stack-Trace einer benutzerdefinierten Ausnahmeklasse auffüllen muss, anstatt native Klassen zu verwenden (die zumindest teilweise zwischengespeichert zu sein scheinen). Bevor wir sagen, dass etwas einseitig schneller oder langsamer ist, wäre es gut, ein Benchmarking durchzuführen.
In Python ist es nicht nur schneller, sondern auch korrekter, etwas zu tun, das möglicherweise eine Ausnahme verursacht, und dann den Fehler zu beheben. Ja, Sie können ein Typensystem erzwingen, aber das widerspricht der Philosophie der Sprache - stattdessen sollten Sie einfach versuchen, die Methode aufzurufen und das Ergebnis abzufangen! (Testen, ob eine Datei beschreibbar ist, ist ähnlich. Schreiben Sie einfach darauf und stellen Sie den Fehler fest.)
Ich habe Zeiten erlebt, in denen es schneller geht, etwas Dummes wie Abfragetabellen zu tun, die nicht vorhanden waren, als herauszufinden, ob eine Tabelle in PHP + MySQL vorhanden ist (die Frage, bei der ich dies vergleiche, ist eigentlich meine einzige akzeptierte Antwort mit negativen Stimmen). .
Trotzdem sollte die Verwendung von Ausnahmen aus mehreren Gründen eingeschränkt werden:
try...catch
Kontrollfluss-Teilnehmer im Allgemeinen (meiner Erfahrung nach) nicht der Philosophie "Code in Try-Block minimieren" folgen.else if
Block ist nicht zulässig . Genug gesagt (und wenn jemand mit einem "Aber sie könnten verschiedene Ausnahmeklassen verwenden" kontert, lautet meine Antwort "Geh in dein Zimmer und komm nicht raus, bis du über das nachgedacht hast, was du gesagt hast.")Exception
bedeutet für den Rest der Welt (wie bei Nichtbefolgung dertry...catch
Kontrollflussphilosophie), dass etwas in einen instabilen (wenn auch möglicherweise wiederherstellbaren) Zustand eingetreten ist. Instabile Zustände sind SCHLECHT und es sollte uns alle nachts wach halten, wenn wir tatsächlich vermeidbare Ausnahmen haben (es schreckt mich tatsächlich heraus, keine Lügen).try...catch
Der Kontrollfluss widerspricht den allgemein als Best Practices angesehenen Methoden. Dies bedeutet, dass jemand, der noch nicht mit einem Projekt vertraut ist, mehr Zeit benötigt, um sich mit dem Projekt vertraut zu machen.try{ obj.openSomething(); /*something which causes exception*/ obj.doStuff(); obj.closeSomething();}catch(Exception e){obj.closeSomething();}
. In einem traditionellerenif...else
SzenariocloseSomething()
ist es weniger wahrscheinlich (wiederum persönliche Erfahrung), dass es sich um einen Kopier- und Einfügejob handelt. (Zugegeben, dieses spezielle Argument hat mehr mit Menschen zu tun, denen ich begegnet bin als mit der eigentlichen Philosophie).quelle
Mein Hauptargument ist, dass die Verwendung von try / catch für die Logik den logischen Fluss unterbricht. Das Verfolgen von "Logik" durch nicht-logische Konstrukte ist (für mich) kontraintuitiv und verwirrend. Ich bin es gewohnt, meine Logik als "Wenn Bedingung dann A sonst B" zu lesen. Die gleiche Aussage wie "Try A catch then execute B" zu lesen, fühlt sich seltsam an. Es wäre noch seltsamer, wenn die Anweisung
A
eine einfache Zuweisung wäre und zusätzlicher Code erforderlich wäre, um eine Ausnahme zu erzwingen, wenn siecondition
falsch ist (und dies würde wahrscheinlichif
ohnehin eine -Anweisung erfordern ).quelle
Nun, nur eine Frage der Etikette, bevor ich, wie Sie sagen, "eine Auseinandersetzung mit ihnen anfange", würde ich Sie freundlich fragen: "Warum verwenden sie die Ausnahmebehandlung an all diesen verschiedenen Stellen?"
Ich meine, es gibt mehrere Möglichkeiten:
... Ich denke, all dies ist gleich wahrscheinlich. Also frag sie einfach nett.
quelle
Try Catch sollte nur zur Ausnahmebehandlung verwendet werden. Dies gilt insbesondere für die Behandlung von Ausnahmen. Ihr Versuchsfang sollte nur erwartete Ausnahmen fangen, ansonsten ist er nicht gut geformt. Wenn Sie catch all verwenden müssen, versuchen Sie catch, dann machen Sie wahrscheinlich etwas falsch.
BEARBEITEN:
Grund: Sie können argumentieren, dass, wenn Sie try catch als bedingten Operator verwenden, wie Sie REAL-Ausnahmen berücksichtigen werden?
quelle
catch
Klausel erfassen, die sich von der Klausel unterscheidet, die "nicht reale" Ausnahmen erfasst?Ausnahme sind, wenn außergewöhnliche Dinge passieren. Funktioniert das Programm gemäß regulärem Workflow außergewöhnlich?
quelle
Grund für die Verwendung von Ausnahmen:
Gründe, keine Ausnahmen zu verwenden:
Schlussendlich:
Ziel ist es, Code zu schreiben, der kommuniziert, was gerade passiert. Ausnahmen können helfen / behindern, je nachdem, was der Code tut. Ein Versuch / Fang in Python für einen KeyError in einer Wörterbuchreferenz ist perfekt (solange Sie die Sprache kennen). Versuch / Fang für denselben KeyError, der fünf Funktionsschichten entfernt ist, ist gefährlich.
quelle
Ich benutze try-> catch als Flusskontrolle in bestimmten Situationen. Wenn Ihre primäre Logik von etwas abhängt, das fehlschlägt, Sie aber nicht einfach eine Ausnahme auslösen und beenden möchten ... Dafür ist ein try-> catch-Block vorgesehen. Ich schreibe viele nicht überwachte serverseitige Unix-Skripte, und es ist weitaus wichtiger, dass sie nicht scheitern, als dass sie hübsch scheitern.
So versucht Plan A, und wenn Plan A sterben, fängt und lief mit Plan B ... Und wenn Plan B ausfällt, schließlich zu Kick - off Plan C, das wird entweder fix eine der ausgefallenen Dienste in A oder B oder Seite mich.
quelle
Dies hängt von der Sprache und möglicherweise von der verwendeten Implementierung ab. Der Standard in C ++ ist, dass Ausnahmen für wirklich außergewöhnliche Ereignisse gespeichert werden sollten. Auf der anderen Seite lautet in Python eine der Richtlinien: " Es ist einfacher, um Vergebung zu bitten als um Erlaubnis ", daher wird die Integration von try / except-Blöcken in die Programmlogik empfohlen.
quelle
Es ist eine schlechte Angewohnheit, aber ich mache es von Zeit zu Zeit in Python. Anstatt zu prüfen, ob eine Datei existiert, versuche ich einfach, sie zu löschen. Wenn es eine Ausnahme auslöst, fange ich und fahre fort zu wissen, dass es nicht da war. <== (nicht unbedingt wahr, wenn ein anderer Benutzer die Datei besitzt, aber ich mache es in einem Benutzerverzeichnis, so dass dies NICHT passieren SOLLTE).
Überbeansprucht ist es jedoch eine schlechte Praxis.
quelle
Ich mag die Art und Weise, wie Apple sie definiert : Ausnahmen gelten nur für Programmiererfehler und schwerwiegende Laufzeitfehler. Verwenden Sie ansonsten Fehlercodes. Es hilft mir zu entscheiden, wann ich es verwenden soll, und ich denke mir: "War das ein Programmiererfehler?"
quelle
Dies hört sich so an, als würde eine Ausnahmebehandlung verwendet, um eine Goto-Logik auf einer Sprache zu erstellen, die keinen Goto-Befehl enthält.
Aus den vielen Gründen, warum goto logic schlecht ist, können Sie sich auf diese Frage beziehen: https://stackoverflow.com/questions/46586/goto-still-considered-harmful
quelle