Effiziente Nutzung von Try / Catch-Blöcken?

21

Sollen Catch-Blöcke zum Schreiben von Logik verwendet werden, z. B. zur Flusskontrolle? Oder nur um Ausnahmen zu werfen? Beeinflusst es die Effizienz oder Wartbarkeit von Code?

Was sind die Nebenwirkungen (falls vorhanden) des Schreibens von Logik in den Catch-Block?

BEARBEITEN:

Ich habe eine Java SDK-Klasse gesehen, in der sie Logik in den catch-Block geschrieben haben. Zum Beispiel (Ausschnitt aus der java.lang.IntegerKlasse):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2 :

Ich habe ein Tutorial durchlaufen, in dem sie es als einen Vorteil beim Schreiben der Logik von Ausnahmefällen innerhalb der Ausnahmen ansehen:

Mit Ausnahmen können Sie den Hauptfluss Ihres Codes schreiben und die Ausnahmefälle an anderer Stelle behandeln.

Gibt es spezielle Richtlinien, wann Logik in einen catch-Block geschrieben werden soll und wann nicht?

HashimR
quelle
12
@Coder Ich denke, du übertreibst ein bisschen. Zumal man es bei vielen der vorhandenen APIs nicht vermeiden kann.
CodesInChaos
8
@Coder: In Java werden Ausnahmen häufig als Mechanismus zur Signalisierung von Problemen verwendet, die Teil des legitimen Codeflusses sind. Wenn Sie beispielsweise versuchen, eine Datei zu öffnen, die fehlschlägt, teilt Ihnen die Java-Bibliothek dies durch Auslösen einer Ausnahme mit, da die APIs, die zum Öffnen einer Datei führen, keinen anderen Mechanismus zum Zurückgeben eines Fehlers haben.
JeremyP
4
@Coder Hängt ab. Ich denke, wenn IO ausgeführt wird oder wenn komplexe Daten analysiert werden, die fast immer korrekt formatiert sind, wird der Code durch das Auslösen einer Ausnahme zum Anzeigen eines Fehlers viel sauberer.
CodesInChaos
1
@Coded Ja, die Ausführung gibt an, dass die Methode Ihnen mitteilt, dass sie nicht in der Lage war, das zu tun, was von ihr verlangt wurde. Ausnahmen sollten jedoch nicht für die Flusssteuerung verwendet werden. Aus diesem Grund verfügt C # auch über eine int.TryParse-Methode, die nicht ausgelöst wird.
Andy
1
@Coder: Das ist völliger Quatsch. Dinge, die auf einer Codeebene außergewöhnlich sind, sind auf einer anderen möglicherweise nicht außergewöhnlich. Sie möchten beispielsweise Fehler- oder Debuginformationen anzeigen oder hinzufügen, bevor Sie fortfahren.
DeadMG

Antworten:

46

Das Beispiel, das Sie zitieren, ist auf ein schlechtes API-Design zurückzuführen (es gibt keinen sauberen Weg, um zu überprüfen, ob ein String eine gültige Ganzzahl ist, es sei denn, Sie versuchen, ihn zu analysieren und die Ausnahme abzufangen).

Auf technischer Ebene sind throw und try / catch Kontrollflusskonstrukte , mit denen Sie den Aufrufstapel hochspringen können, nicht mehr und nicht weniger. Das Hochspringen des Aufrufstapels verbindet implizit Code, der in der Quelle nicht nahe beieinander liegt, was für die Wartbarkeit schlecht ist . Es sollte also nur verwendet werden, wenn Sie dies tun müssen, und die Alternativen sind noch schlimmer. Der allgemein akzeptierte Fall, in dem die Alternativen schlechter sind, ist die Fehlerbehandlung (spezielle Rückkehrcodes, die überprüft und manuell auf jeder Ebene des Aufrufstapels weitergegeben werden müssen).

Wenn Sie einen Fall haben, in dem die Alternativen schlechter sind (und Sie wirklich alle sorgfältig überlegt haben), dann würde ich sagen, dass die Verwendung von werfen und versuchen / fangen für den Kontrollfluss in Ordnung ist. Dogma ist kein guter Ersatz für Urteilsvermögen.

Michael Borgwardt
quelle
1
+1, gute Punkte gemacht. Ich denke, dass einige Verarbeitung im Fang wie Vorbereitung einer Fehlermeldung und Fehlerprotokollierung gültig sein kann. Das Freigeben von teuren Ressourcen wie Datenbankverbindungen und in (.NET) COM-Verweisen könnte dem Block "finally" hinzugefügt werden.
NoChance
@EmmadKareem Ich habe darüber nachgedacht, Ressourcen freizugeben, aber normalerweise muss man diese ohnehin irgendwann auch ohne Fehlersituation freigeben. So könnten Sie sich wiederholen. Das Vorbereiten einer Protokollnachricht ist natürlich akzeptabel.
Scarfridge
@MichaelBorgwardt Ich denke, das API-Design ist nicht so schlecht (obwohl es besser sein könnte). Der Versuch, eine ungültige Zeichenfolge zu analysieren, ist eindeutig eine Fehlerbedingung und damit genau der von Ihnen erwähnte "allgemein akzeptierte Fall". Einverstanden, eine Methode, um festzustellen, ob eine Zeichenfolge syntaktisch korrekt ist, ist praktisch (hier könnte es besser sein). Es ist jedoch nicht möglich, alle zu zwingen, diese Methode aufzurufen, bevor das eigentliche Parsen durchgeführt wird, und wir müssen den Fehler behandeln. Das Mischen des tatsächlichen Ergebnisses und des Fehlercodes ist unangenehm und Sie würden trotzdem die Ausnahme auslösen. Aber vielleicht eine Laufzeitausnahme.
Scarfridge
3
+1 für diesen letzten Satz.
FrustratedWithFormsDesigner
2
@scarfridge: Ich stimme dir vollkommen zu :) Das Fehlen einer booleschen, zurückgegebenen isInteger (String) -Methode ist alles, was ich mit "schlechtem API-Design" meine
Michael Borgwardt
9

Dies ist in der Regel abhängig von Sprache und Paradigma.

Die meiste Arbeit erledige ich in Java (und manchmal in C ++). Die Tendenz besteht darin, Ausnahmen nur für Ausnahmebedingungen zu verwenden. Bei Stack Overflow gibt es einige Fragen zum Performance-Overhead von Ausnahmen in Java , und wie Sie sehen können, ist dies nicht unbedingt zu vernachlässigen. Es gibt auch andere Bedenken, wie zum Beispiel die Lesbarkeit und Wartbarkeit des Codes. Bei ordnungsgemäßer Verwendung können Ausnahmen die Fehlerbehandlung an die entsprechenden Komponenten delegieren.

In Python herrscht jedoch die Vorstellung, dass es einfacher ist, um Verzeihung zu bitten, als um Erlaubnis. In der Python-Community wird dies als EAFP bezeichnet. Dies steht im Gegensatz zum LBYL- Ansatz (Look Before You Jump ) in C-Sprachen.

Thomas Owens
quelle
2
+1 für das Erwähnen des Overheads mit Ausnahmen. Ich mache .NET und (zumindest auf meinem Computer) konnte ich sogar fühlen, wenn eine Ausnahme aufgrund der Dauer, die in einigen Fällen im Vergleich zu anderen normalen Vorgängen benötigt wird, bevorsteht.
NoChance
2
@EmmadKareem Nur wenn Sie einen Debugger angeschlossen haben. Sie können mehrere Tausend pro Sekunde werfen, ohne einen Debugger zu haben.
CodesInChaos
@ CodeInChaos, Sie sind richtig. Wie würde ich wissen, dass ich so einen sauberen Code schreibe, der zur Laufzeit nie einen bekommt :)
NoChance
@EmmadKareem Abhängig davon, wie eine Netzwerkanwendung geschrieben wird, werden Verbindungs- oder Protokollfehler mit einer Ausnahme behandelt. Ausnahmen müssen in einer solchen Anwendung relativ schnell sein, um triviale DoS-Angriffe zu vermeiden.
CodesInChaos
@ CodeInChaos, das ist gut zu wissen. Ich habe meinen letzten Kommentar veräppelt.
NoChance
9

Das ist nicht die beste Art, über Try / Catch-Blöcke nachzudenken. Die Art und Weise, über Try / Catch-Blöcke nachzudenken, ist folgende: Es ist ein Fehler aufgetreten. Hier ist der beste Ort, um diesen bestimmten Fehler zu beheben. Das kann die nächste Codezeile sein, es können zwanzig Ebenen in der Aufrufkette sein. Wo immer es ist, dort sollte es sein.

Was der catch-Block macht, hängt vom Fehler ab und was Sie dagegen tun können. Manchmal kann es einfach ignoriert werden (Fehler beim Löschen einer Arbeitsdatei ohne wichtige Daten), manchmal wird es verwendet, um den Rückgabewert einer Funktion auf true oder false zu setzen (Java-Methode int parse), manchmal wird das Programm beendet . Das hängt alles vom Fehler ab.

Der wichtige Teil, den man verstehen muss, ist: catch block == Ich weiß, wie man damit umgeht.

jmoreno
quelle
6

Catch-Blöcke sollten nur für die Behandlung von Ausnahmen verwendet werden, nichts anderes und niemals für den Kontrollfluss. In jeder Situation, in der Sie Ausnahmen zum Verwalten des Datenflusses verwenden möchten, sollten Sie die Voraussetzungen überprüfen.

Der Ablauf mit Ausnahmen ist langsam und semantisch inkorrekt.

Tom Squires
quelle
4
Ich weiß, was Sie sagen, aber es ist erwähnenswert, dass alles, was Ausnahmen bewirken, der Programmfluss ist - nur, dass es nur zur Steuerung eines außergewöhnlichen Fehlerflusses verwendet werden sollte ...
Max
Gibt es keine spezifischen Richtlinien, wann Logik in einen catch-Block geschrieben werden soll und wann nicht?
HashimR
4
Javas Zahlenparser und Textformatierer sind bekannte Beispiele für problematische Ausnahmeverwendung: Die einzige vernünftige Möglichkeit, zu überprüfen, ob eine Voraussetzung (dass eine Zeichenfolge eine analysierbare Zahl darstellt), besteht darin, sie zu analysieren in der Praxis? Sie müssen die Ausnahme abfangen und den Datenfluss damit verwalten, unabhängig davon, ob dies als semantisch inkorrekt angesehen wird.
Joonas Pulakka
@JoonasPulakka Ich denke, Ihr Kommentar für Zahlenparser und Textformatierer ist eine Antwort in voller Größe auf diese Frage
Mücke
5

Sollen Catch-Blöcke zum Schreiben von Logik verwendet werden, z. B. zur Flusskontrolle? Oder nur um Ausnahmen zu werfen? Beeinflusst es die Effizienz oder Wartbarkeit von Code?

Vergessen Sie zunächst den Begriff "Ausnahmen sollten für außergewöhnliche Bedingungen verwendet werden". Sorgen Sie sich auch nicht mehr um die Effizienz, bis Sie Code haben, der nicht akzeptabel funktioniert, und Sie gemessen haben, um zu wissen, wo das Problem liegt.

Code ist am einfachsten zu verstehen, wenn Aktionen in einer einfachen Reihenfolge ohne Bedingungen ausgeführt werden. Ausnahmen verbessern die Wartbarkeit, indem Fehlerprüfungen aus dem normalen Ablauf entfernt werden. Es spielt keine Rolle, ob die Ausführung zu 99,9% der Zeit oder zu 50% der Zeit oder zu 20% der Zeit dem normalen Ablauf folgt.

Auslösen einer Ausnahme, wenn eine Funktion keinen Wert zurückgeben kann, wenn eine Prozedur die erwartete Aktion nicht ausführen kann oder wenn ein Konstruktor kein verwendbares Objekt erstellen kann. Auf diese Weise können Programmierer davon ausgehen, dass eine Funktion immer ein verwendbares Ergebnis liefert, eine Prozedur die angeforderte Aktion immer abschließt und ein erstelltes Objekt immer in einem verwendbaren Zustand ist.

Das größte Problem, das ich mit dem Code zur Ausnahmebehandlung sehe, ist, dass Programmierer Try / Catch-Blöcke schreiben, wenn sie dies nicht sollten. Wenn beispielsweise in den meisten Webanwendungen eine Datenbankanforderung eine Ausnahme auslöst, kann auf dem Controller nichts ausgeführt werden. Eine generische Fangklausel auf höchster Ebene reicht aus. Dann kann der Controller-Code die Möglichkeit, dass die Festplatte abgestürzt ist oder die Datenbank offline ist, oder was auch immer, auf selige Weise ignorieren.

Kevin Cline
quelle
1

Catch-Blöcke sollten nicht zum Schreiben von Codelogik verwendet werden. Sie sollten nur zur Fehlerbehandlung verwendet werden. Ein Beispiel ist (1) Bereinigen aller zugewiesenen Ressourcen, (2) Drucken einer nützlichen Nachricht und (3) ordnungsgemäßes Beenden.

Es ist richtig, dass Ausnahmen missbraucht werden und unerwünschte gotoAuswirkungen haben können. Aber das macht sie nicht unbrauchbar. Bei richtiger Verwendung können sie viele Aspekte Ihres Codes verbessern.

Sakisk
quelle