it-swarm.dev

Argumente für oder gegen die Verwendung von Try / Catch als logische Operatoren

Ich habe gerade einen schönen Code in unserer Unternehmens-App entdeckt, der Try-Catch-Blöcke als logische Operatoren verwendet.
Bedeutung: "Machen Sie einen Code, wenn dies diesen Fehler auslöst, machen Sie diesen Code, aber wenn dies diesen Fehler auslöst, machen Sie stattdessen diese dritte Sache.".
Es verwendet "Endlich" als die "sonst" -Anweisung, die es erscheint.
Ich weiß, dass dies von Natur aus falsch ist, aber bevor ich mich für einen Kampf entschieden habe, habe ich auf einige gut durchdachte Argumente gehofft.
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.

38
James P. Wright

Die Ausnahmebehandlung ist in der Regel eine teure Methode zur Ablaufsteuerung (sicherlich für C # und Java).

Die Laufzeit erledigt eine Menge Arbeit, wenn ein Ausnahmeobjekt erstellt wird - das Zusammenstellen des Stack-Trace, das Herausfinden, wo die Ausnahme behandelt wird und vieles mehr.

All dies kostet Speicher- und CPU-Ressourcen, die nicht erweitert werden müssen, wenn Flusssteuerungsanweisungen für die Flusssteuerung verwendet werden.

Zusätzlich gibt es ein semantisches Problem. Ausnahmen gelten für Ausnahmesituationen, nicht für die normale Flusskontrolle. Die Ausnahmebehandlung sollte für die Behandlung unerwarteter/außergewöhnlicher Situationen verwendet werden, nicht als normaler Programmablauf, da ansonsten eine nicht erfasste Ausnahme viel weniger aussagt.

Abgesehen von diesen beiden gibt es die Frage, ob andere den Code lesen. Die Verwendung von Ausnahmen auf diese Weise wird von den meisten Programmierern nicht erwartet. Daher leidet die Lesbarkeit und die Verständlichkeit Ihres Codes. Wenn man "Ausnahme" sieht, denkt man - etwas Schlimmes ist passiert, etwas, das normalerweise nicht passieren soll. Die Verwendung von Ausnahmen auf diese Weise ist einfach verwirrend.

54
Oded

Ich habe gerade einen schönen Code in unserer Unternehmens-App entdeckt, der Try-Catch-Blöcke als logische Operatoren verwendet. Das heißt: "Machen Sie Code, wenn dies diesen Fehler auslöst, machen Sie diesen Code, aber wenn dies diesen Fehler auslöst, machen Sie stattdessen diese dritte Sache." Es verwendet "Endlich" als die "else" -Anweisung, die angezeigt wird. Ich weiß, dass dies von Natur aus falsch ist ...

Wie kannst du das Wissen? Ich habe all diese Art von "Wissen" aufgegeben und glaube jetzt nur, dass der einfachste Code der beste ist. Angenommen, Sie möchten eine Zeichenfolge in eine Option konvertieren, die leer ist, wenn die Analyse fehlschlägt. Es ist nichts falsch mit:

try { 
    return Optional.of(Long.valueOf(s)); 
} catch (NumberFormatException) { 
    return Optional.empty(); 
}

Ich bin völlig anderer Meinung als die übliche Interpretation von "Ausnahmen gelten für außergewöhnliche Bedingungen". Wenn eine Funktion keinen verwendbaren Wert zurückgeben kann oder eine Methode ihre Nachbedingungen nicht erfüllen kann, wird eine Ausnahme ausgelöst. Es spielt keine Rolle, wie oft diese Ausnahmen ausgelöst werden, bis ein nachgewiesenes Leistungsproblem vorliegt.

Ausnahmen vereinfachen den Code, indem die Fehlerbehandlung vom normalen Ablauf getrennt wird. Schreiben Sie einfach den einfachsten Code. Wenn es einfacher ist, try-catch zu verwenden oder eine Ausnahme auszulösen, tun Sie dies.

Ausnahmen vereinfachen das Testen, indem die Anzahl der Pfade durch den Code verringert wird. Eine Funktion ohne Verzweigungen wird entweder abgeschlossen oder eine Ausnahme auslösen. Eine Funktion mit mehreren if -Anweisungen zum Überprüfen auf Fehlercodes hat viele mögliche Pfade. Es ist sehr einfach, eine der Bedingungen falsch zu verstehen oder eine vollständig zu vergessen, so dass einige Fehlerbedingungen ignoriert werden.

16
kevin cline

Debug- und Wartungsarbeiten sind sehr schwierig, wenn der Kontrollfluss mit Ausnahmen ausgeführt wird.

Ausnahmen sind von Natur aus als Mechanismus zur Änderung des normalen Kontrollflusses Ihres Programms gedacht - um ungewöhnliche Aktivitäten auszuführen und ungewöhnliche Nebenwirkungen zu verursachen, um aus einer besonders engen Bindung herauszukommen, die nicht mit weniger Komplikationen bewältigt werden kann meint. Ausnahmen sind außergewöhnlich. Dies bedeutet, dass abhängig von der jeweiligen Umgebung, in der Sie arbeiten, die Verwendung einer Ausnahme für den regulären Kontrollfluss Folgendes verursachen kann:

  • Ineffizienz Die zusätzlichen Rahmen, durch die die Umgebung springen muss, um die für Ausnahmen erforderlichen relativ schwierigen Kontextänderungen sicher durchzuführen, erfordern Instrumentierung und Ressourcen.

  • Debug-Schwierigkeiten 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 Programmverlauf verlieren, der für das Verständnis des Laufzeitverhaltens relevant ist

  • Wartungsprobleme Der Ausführungsfluss ist durch Ausnahmesprünge schwer zu verfolgen. Darüber hinaus kann eine Ausnahme aus dem Black-Box-Code ausgelöst werden, der sich beim Auslösen einer Ausnahme möglicherweise nicht leicht verständlich verhält.

  • Schlechte Entwurfsentscheidungen Auf diese Weise erstellte Programme fördern eine Geisteshaltung, die sich in den meisten Fällen nicht leicht auf eine elegante Lösung von Problemen abbilden lässt. Die Komplexität des endgültigen Programms hält den Programmierer davon ab, seine Ausführung vollständig zu verstehen, und ermutigt dazu, Entscheidungen zu treffen, die zu kurzfristigen Verbesserungen mit hohen langfristigen Kosten führen.

13
blueberryfields

Ich habe dieses Muster mehrmals gesehen.

Es gibt 2 Hauptprobleme:

  • Es ist extrem teuer (Instanziierung des Ausnahmeobjekts, Sammeln des Aufrufstapels usw.). Einige Compiler sind möglicherweise tatsächlich in der Lage, es zu optimieren, aber ich würde in diesem Fall nicht damit rechnen, da Ausnahmen nicht für eine solche Verwendung vorgesehen sind und Sie daher nicht erwarten können, dass die Benutzer es optimieren.
  • Die Verwendung von Ausnahmen für den Kontrollfluss ist in der Tat ein Sprung, ähnlich wie bei einem goto. Sprünge werden aus mehreren Gründen als schädlich angesehen. 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.
10
back2dos

Manchmal sind Ausnahmen am schnellsten. Ich habe Fälle gesehen, in denen Nullobjektausnahmen sogar in Java als bei Verwendung von Kontrollstrukturen schneller waren) (Ich kann die Studie derzeit nicht zitieren, daher müssen Sie mir vertrauen). Das Problem kommt herein, wenn Java muss sich tatsächlich die Zeit nehmen und den Stack-Trace einer benutzerdefinierten Ausnahmeklasse füllen, anstatt native zu verwenden (die zumindest teilweise zwischengespeichert zu sein scheinen). Bevor Sie sagen, dass etwas ist einseitig schneller oder langsamer wäre ein Benchmarking gut.

In Python ist es nicht nur schneller, sondern es ist auch viel korrekter, etwas zu tun, das eine Ausnahme verursachen könnte, und dann den Fehler zu behandeln. Ja, Sie können ein Typsystem erzwingen, aber das widerspricht die Philosophie der Sprache - stattdessen sollten Sie einfach versuchen, die Methode aufzurufen und das Ergebnis abzufangen! (Das Testen, ob eine Datei beschreibbar ist, ist ähnlich - versuchen Sie einfach, darauf zu schreiben und den Fehler abzufangen).

Ich habe Zeiten gesehen, in denen es schneller ist, etwas Dummes zu tun, wie Abfragetabellen, die es nicht gab, als herauszufinden, ob eine Tabelle in PHP + MySQL existiert (die Frage wo ich das Benchmarking mache, ist dies tatsächlich meine nur akzeptierte Antwort mit negativen Stimmen).

Trotzdem sollte die Verwendung von Ausnahmen aus mehreren Gründen eingeschränkt werden:

  1. Versehentliches Verschlucken verschachtelter Ausnahmen. Dies ist schwerwiegend. Wenn Sie eine tief verschachtelte Ausnahme bemerken, mit der jemand anderes umgehen möchte, haben Sie gerade Ihrem Programmierkollegen (vielleicht sogar Ihnen!) In den Fuß geschossen.
  2. Tests werden nicht offensichtlich. Ein Codeblock, der eine Ausnahme enthält, kann eines von mehreren Fehlern aufweisen. Ein Boolescher Wert hingegen ist zwar theoretisch ärgerlich beim Debuggen, im Allgemeinen jedoch nicht. Dies gilt insbesondere, da die try...catch - Kontrollfluss-Anhänger im Allgemeinen (meiner Erfahrung nach) nicht der Philosophie "Code im Try-Block minimieren" folgen.
  3. Es erlaubt keinen else if Block. Genug gesagt (und wenn jemand mit einem "Aber er könnte verschiedene Ausnahmeklassen verwenden" kontert, lautet meine Antwort "Geh in dein Zimmer und komm nicht raus, bis du darüber nachgedacht hast, was du gesagt hast. ")
  4. Es ist grammatikalisch irreführend.Exception bedeutet für den Rest der Welt (wie bei Nichteinhaltung der Kontrollflussphilosophie try...catch), Dass etwas hat in einen instabilen (wenn auch möglicherweise wiederherstellbaren) Zustand eingetreten. Instabile Zustände sind [~ # ~] schlecht [~ # ~] und es sollte uns alle nachts wach halten wenn wir tatsächlich vermeidbare Ausnahmen haben (es macht mir tatsächlich Angst, keine Lügen).
  5. Es entspricht nicht dem üblichen Codierungsstil. Unsere Aufgabe, sowohl mit unserem Code als auch mit unseren Benutzeroberflächen, ist es, die Welt so offensichtlich wie möglich zu machen. try...catch Der Kontrollfluss widerspricht den allgemein als Best Practices geltenden Methoden. Dies bedeutet, dass jemand, der neu in einem Projekt ist, mehr Zeit benötigt, um das Projekt zu lernen, was eine Erhöhung der Anzahl der Arbeitsstunden ohne jeglichen Gewinn bedeutet.
  6. Dies führt häufig zu einer Codeduplizierung. Schließlich müssen Blöcke, obwohl dies nicht unbedingt erforderlich ist, alle baumelnden Zeiger auflösen, die vom unterbrochenen try-Block offen gelassen wurden. Versuchen Sie es also mit Blöcken. Dies bedeutet, dass Sie eine try{ obj.openSomething(); /*something which causes exception*/ obj.doStuff(); obj.closeSomething();}catch(Exception e){obj.closeSomething();} haben können. In einem traditionelleren if...else - Szenario ist es weniger wahrscheinlich, dass closeSomething() (wiederum persönliche Erfahrung) ein Kopier- und Einfügejob ist. (Zugegeben, dieses spezielle Argument hat mehr mit Menschen zu tun, die ich getroffen habe, als mit der eigentlichen Philosophie selbst).
9
cwallenpoole

Mein Hauptargument ist, dass die Verwendung von try/catch für Logik den logischen Fluss unterbricht. "Logik" durch nichtlogische Konstrukte zu folgen, ist (für mich) kontraintuitiv und verwirrend. Ich bin es gewohnt, meine Logik als "wenn Bedingung dann A sonst B" zu lesen. Es fühlt sich komisch an, die gleiche Aussage wie "A fangen, dann B ausführen" zu lesen. Es wäre noch seltsamer, wenn die Anweisung A eine einfache Zuweisung ist und zusätzlichen Code benötigt, um eine Ausnahme zu erzwingen, wenn condition falsch ist (und dies würde wahrscheinlich eine if- Anweisung erfordern wie auch immer).

Nun, nur eine Frage der Etikette, bevor ich, wie Sie sagen, "einen Streit mit ihnen anfange", würde ich freundlich fragen: "Warum verwenden sie die Ausnahmebehandlung an all diesen verschiedenen Orten?"

Ich meine, es gibt mehrere Möglichkeiten:

  1. sie sind inkompetent
  2. es gibt einen durchaus gültigen Grund für das, was sie getan haben, der möglicherweise nicht auf den ersten Blick erscheint.
  3. manchmal ist es eine Frage des Geschmacks und kann einen komplexen Programmablauf vereinfachen.
  4. Es ist ein Relikt der Vergangenheit, dass sich noch niemand verändert hat und sie würden sich freuen, wenn jemand es tut

... Ich denke, all dies ist gleich wahrscheinlich. Also frag sie einfach nett.

2
dagnelies

Grund für die Verwendung von Ausnahmen:

  1. Wenn Sie vergessen, einen außergewöhnlichen Umstand zu erfassen, stirbt Ihr Programm und der Stack-Trace zeigt Ihnen genau, warum. Wenn Sie vergessen, den Rückgabewert für einen Ausnahmefall zu behandeln, können Sie nicht sagen, wie weit Ihr Programm entfernt ein falsches Verhalten aufweist.
  2. Die Verwendung von Rückgabewerten funktioniert nur, wenn Sie einen Sentinel-Wert zurückgeben können. Was werden Sie tun, wenn alle möglichen Rückgabewerte bereits gültig sind?
  3. Eine Ausnahme enthält zusätzliche Informationen darüber, was passiert ist, was nützlich sein kann.

Gründe, Ausnahmen nicht zu verwenden:

  1. Viele Sprachen sind nicht dafür ausgelegt, Ausnahmen schnell zu machen.
  2. Ausnahmen, die sich mehrere Schichten über den Stapel erstrecken, können einen inkonsistenten Zustand hinterlassen

Schlussendlich:

Das Ziel ist es, Code zu schreiben, der kommuniziert, was los ist. 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) ist ein Versuch/Fang für denselben KeyError, der fünf Funktionsebenen entfernt ist, gefährlich.

1
Winston Ewert

Try Catch sollte nur zur Ausnahmebehandlung verwendet werden. Insbesondere die Behandlung spezieller Ausnahmen. Ihr Versuch sollte nur erwartete Ausnahmen fangen, andernfalls ist er nicht gut geformt. Wenn Sie einen Fang verwenden müssen, versuchen Sie es mit catch, dann machen Sie wahrscheinlich etwas falsch.

BEARBEITEN:

Grund: Sie können argumentieren, dass Sie REAL-Ausnahmen berücksichtigen, wenn Sie try catch als bedingten Operator verwenden.

1
AJC

Ausnahme sind, wenn außergewöhnliche Dinge passieren. Funktioniert das Programm gemäß dem regulären Workflow außergewöhnlich?

1
jfrobishow

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 ", so dass die Integration von try/exception blockiert Programmlogik wird empfohlen.

0

In bestimmten Situationen verwende ich try-> catch als Flusskontrolle. 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 gedacht. Ich schreibe viele nicht überwachte serverseitige Unix-Skripte, und es ist weitaus wichtiger, dass sie nicht versagen, als dass sie hübsch versagen.

Also versuchen Plan A, und wenn Plan A stirbt, fangen und laufen mit Plan B ... Und wenn Plan B fehlschlägt Verwenden Sie endlich, um Plan C zu starten, der entweder einen der fehlgeschlagenen Dienste in A oder B behebt oder mich anruft.

0
Satanicpuppy