Warum try verwenden ... endlich ohne catch-Klausel?

79

Die klassische Art zu programmieren ist mit try ... catch. Wann ist es angebracht, tryohne zu verwenden catch?

In Python erscheint Folgendes legal und kann sinnvoll sein:

try:
  #do work
finally:
  #do something unconditional

Der Code hat jedoch catchnichts. Ähnlich könnte man in Java denken, es wäre wie folgt:

try {
    //for example try to get a database connection
}
finally {
  //closeConnection(connection)
}

Es sieht gut aus und plötzlich muss ich mich nicht mehr um Ausnahmetypen usw. kümmern. Wenn dies eine gute Praxis ist, wann ist sie eine gute Praxis? Was sind alternativ die Gründe, warum dies keine gute Praxis oder nicht legal ist? (Ich habe die Quelle nicht kompiliert. Ich frage danach, da es sich um einen Syntaxfehler für Java handeln könnte. Ich habe überprüft, ob Python sicher kompiliert.)

Ein damit verbundenes Problem, auf das ich gestoßen bin, ist folgendes: Ich schreibe die Funktion / Methode weiter, an deren Ende etwas zurückgegeben werden muss. Es kann sich jedoch an einem Ort befinden, der nicht erreicht werden sollte und ein Rückgabepunkt sein muss. Also, auch wenn ich die obigen Ausnahmen behandle, gebe ich immer NULLnoch eine leere Zeichenkette zurück oder irgendwann im Code, die nicht erreicht werden sollte, oft das Ende der Methode / Funktion. Ich habe es immer geschafft, den Code so umzustrukturieren, dass er es nicht muss return NULL, da dies absolut weniger als eine gute Übung zu sein scheint.

Niklas Rosencrantz
quelle
4
Setzen Sie in Java die return-Anweisung am Ende des try-Blocks.
Kevin Cline
2
Ich bin traurig, dass try..finally und try..catch beide das Schlüsselwort try verwenden. Abgesehen davon, dass beide mit try beginnen, handelt es sich um zwei völlig unterschiedliche Konstrukte.
Pieter B
3
try/catchist nicht "die klassische Art zu programmieren". Es ist die klassische C ++ - Art zu programmieren, da C ++ kein richtiges try / finally-Konstrukt besitzt, was bedeutet, dass Sie garantierte reversible Zustandsänderungen mit hässlichen Hacks unter Beteiligung von RAII implementieren müssen. Aber anständige OO-Sprachen haben dieses Problem nicht, denn sie bieten try / finally. Es wird für einen ganz anderen Zweck verwendet als try / catch.
Mason Wheeler
3
Ich sehe es viel mit externen Verbindungsressourcen. Sie möchten die Ausnahme, müssen aber sicherstellen, dass Sie keine offene Verbindung usw. belassen. Wenn Sie sie abfangen, werfen Sie sie in einigen Fällen einfach auf die nächste Ebene.
Rig
4
@MasonWheeler "Ugly hacks " bitte erklären Sie, was schlecht ist, wenn ein Objekt seine eigene Bereinigung durchführt?
Baldrickk

Antworten:

146

Es hängt davon ab, ob Sie mit den Ausnahmen umgehen können, die an dieser Stelle ausgelöst werden können oder nicht.

Wenn Sie die Ausnahmen lokal behandeln können, sollten Sie dies tun, und es ist besser, den Fehler so nahe wie möglich an der Stelle zu behandeln, an der er ausgelöst wird.

Wenn Sie nicht lokal damit umgehen können, try / finallyist es durchaus sinnvoll, nur einen Block zu haben - vorausgesetzt, Sie müssen Code ausführen, unabhängig davon, ob die Methode erfolgreich war oder nicht. Zum Beispiel (aus Neils Kommentar ) ist das Öffnen eines Streams und das Übergeben dieses Streams an eine innere Methode zum Laden ein hervorragendes Beispiel dafür, wann Sie try { } finally { }die finally-Klausel verwenden müssen, um sicherzustellen, dass der Stream unabhängig vom Erfolg oder Erfolg geschlossen wird Fehler beim Lesen.

Sie benötigen jedoch weiterhin irgendwo in Ihrem Code einen Ausnahmebehandler - es sei denn, Sie möchten, dass Ihre Anwendung vollständig abstürzt. Es hängt von der Architektur Ihrer Anwendung ab, wo genau sich dieser Handler befindet.

ChrisF
quelle
8
"und es ist besser, den Fehler so nahe wie möglich an der Stelle zu behandeln, an der er ausgelöst wird." Äh, es kommt darauf an. Wenn Sie die Mission sozusagen wiederherstellen und dennoch abschließen können, ja natürlich. Wenn dies nicht möglich ist, lassen Sie die Ausnahme lieber ganz nach oben, wo (wahrscheinlich) ein Benutzereingriff erforderlich ist, um die Ereignisse zu behandeln.
Wird
13
@will - deshalb habe ich den Ausdruck "so gut wie möglich" verwendet.
ChrisF
8
Das Öffnen einen Stream und Leiten dieses Stromes zu einem inneren Verfahren zu ladende ist ein ausgezeichnetes Beispiel dafür , wann Sie brauchen würden: Gute Antwort, aber ich würde ein Beispiel hinzufügen try { } finally { }, unter Ausnutzung der finally - Strom , um sicherzustellen , schließlich geschlossen , unabhängig von Erfolg / Fehler.
Neil
weil manchmal der ganze Weg oben so nah ist, wie man kann
Newtopian
"nur ein Versuch / endlich Block ist völlig vernünftig" suchte genau nach dieser Antwort. danke @ ChrisF
Neal Aise
36

Der finallyBlock wird für Code verwendet, der immer ausgeführt werden muss, unabhängig davon, ob eine Fehlerbedingung (Ausnahme) aufgetreten ist oder nicht.

Der Code im finallyBlock wird ausgeführt, nachdem der tryBlock abgeschlossen ist, und, wenn eine festgestellte Ausnahme aufgetreten ist, nachdem der entsprechende catchBlock abgeschlossen ist. Es wird immer ausgeführt, auch wenn im Block tryoder eine nicht erfasste Ausnahme aufgetreten ist catch.

Der finallyBlock wird normalerweise zum Schließen von Dateien, Netzwerkverbindungen usw. verwendet, die im tryBlock geöffnet wurden . Der Grund dafür ist, dass die Datei- oder Netzwerkverbindung geschlossen werden muss, ob der Vorgang mit dieser Datei- oder Netzwerkverbindung erfolgreich war oder fehlgeschlagen ist.

Es sollte darauf geachtet werden, dass der finallyBlock selbst keine Ausnahme auslöst. Stellen Sie beispielsweise doppelt sicher, dass Sie alle Variablen auf nullusw. überprüfen .

yfeldblum
quelle
8
+1: Es ist idiomatisch für "muss aufgeräumt werden". Die meisten Verwendungen von try-finallykönnen durch eine withAussage ersetzt werden.
S.Lott,
12
Verschiedene Sprachen bieten äußerst nützliche sprachspezifische Verbesserungen für das try/finallyKonstrukt. C # hat using, Python hat with, etc.
yfeldblum
5
@yfeldblum - es gibt einen subtilen Unterschied zwischen usingund try-finally, da die DisposeMethode nicht vom usingBlock aufgerufen wird , wenn im IDisposableKonstruktor des Objekts eine Ausnahme auftritt . try-finallyErmöglicht das Ausführen von Code, auch wenn der Konstruktor des Objekts eine Ausnahme auslöst.
Scott Whitlock
5
@ScottWhitlock: Das ist eine gute Sache? Was möchten Sie tun, um eine Methode für ein nicht konstruiertes Objekt aufzurufen? Das ist eine Milliarde Arten von Schlimmem.
DeadMG
1
@DeadMG: Siehe auch stackoverflow.com/q/14830534/18192
Brian
17

Ein Beispiel, in dem try ... finally without a catch-Klausel in Java angemessen (und noch idiomatischer ) ist, ist die Verwendung von Lock in Concurrent Utilities Locks-Paket.

  • In der API-Dokumentation wird dies folgendermaßen erklärt und begründet (die fett gedruckte Schrift im Zitat stammt von mir):

    ... Das Fehlen blockstrukturierter Sperren beseitigt die automatische Freigabe von Sperren, die bei synchronisierten Methoden und Anweisungen auftreten. In den meisten Fällen sollte die folgende Redewendung verwendet werden :

     Lock l = ...;
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock();
     }

    Wenn das Sperren und Entsperren in verschiedenen Bereichen erfolgt, muss sichergestellt werden, dass der gesamte Code, der ausgeführt wird, während das Schloss gehalten wird, durch try-finally oder try-catch geschützt ist, um sicherzustellen, dass das Schloss bei Bedarf freigegeben wird .

Mücke
quelle
Kann ich das l.lock()mal probieren? try{ l.lock(); }finally{l.unlock();}
RMachnik
1
technisch kann man. Ich habe es nicht dort platziert, weil es semantisch weniger sinnvoll ist. tryin diesem Ausschnitt Ressourcenzugriff wickeln soll, warum es mit keinem Zusammenhang etwas verschmutzen zu diesen
gnat
4
Sie können, aber wenn dies l.lock()fehlschlägt, wird der finallyBlock weiterhin ausgeführt, wenn er l.lock()sich innerhalb des tryBlocks befindet. Wenn Sie dies wie von gnat vorgeschlagen tun, wird der finallyBlock nur ausgeführt, wenn bekannt ist, dass die Sperre aktiviert wurde.
Wtrmute
12

Auf einer grundlegenden Ebene catchund finallylösen Sie zwei verwandte, aber unterschiedliche Probleme:

  • catch wird verwendet, um ein Problem zu behandeln, das von dem aufgerufenen Code gemeldet wurde
  • finally wird verwendet, um Daten / Ressourcen zu bereinigen, die der aktuelle Code erstellt / geändert hat, unabhängig davon, ob ein Problem aufgetreten ist oder nicht

Also beide sind irgendwie Probleme (Ausnahmen) verwendet, aber das ist so ziemlich alles , was sie gemeinsam haben.

Ein wichtiger Unterschied besteht darin, dass sich der finallyBlock in derselben Methode befinden muss , in der die Ressourcen erstellt wurden (um Ressourcenlecks zu vermeiden), und nicht im Aufrufstapel auf eine andere Ebene verschoben werden kann.

Das ist catchjedoch eine andere Sache: Der richtige Ort dafür hängt davon ab, wo Sie die Ausnahme tatsächlich behandeln können . Es hat keinen Sinn, eine Ausnahme an einer Stelle abzufangen, an der Sie nichts dagegen tun können. Deshalb ist es manchmal besser, sie einfach fallen zu lassen.

Joachim Sauer
quelle
2
Nitpick: "... der finally-Block muss sich in derselben Methode befinden, in der die Ressourcen erstellt wurden ..." . Es ist auf jeden Fall eine gute Idee , dies so zu tun, da es einfacher ist, zu erkennen, dass es kein Ressourcenleck gibt. Dies ist jedoch keine notwendige Voraussetzung. dh du musst es nicht so machen. Sie können die Ressource in finallyeiner (statisch oder dynamisch) umschließenden try-Anweisung freigeben ... und trotzdem 100% dicht sein.
Stephen C
6

@yfeldblum hat die richtige Antwort: try-finally ohne catch-Anweisung sollte normalerweise durch ein geeignetes Sprachkonstrukt ersetzt werden.

In C ++ werden RAII und Konstruktoren / Destruktoren verwendet. in Python ist es eine withAussage; und in C # ist es eine usingAussage.

Diese sind fast immer eleganter, da sich der Initialisierungs- und Finalisierungscode an einer Stelle (dem abstrakten Objekt) und nicht an zwei Stellen befindet.

Neil G
quelle
2
Und in Java sind es ARM-Blöcke
MarkJ
2
Angenommen, Sie haben ein schlecht gestaltetes Objekt (z. B. eines, das IDisposable in C # nicht ordnungsgemäß implementiert), das nicht immer eine praktikable Option ist.
mootinator
1
@mootinator: Kannst du nicht von dem schlecht gestalteten Objekt erben und es reparieren?
Neil G
2
Oder Einkapselung? Bah. Ich meine ja natürlich.
Mootinator
4

In vielen Sprachen wird eine finallyAnweisung auch nach der return-Anweisung ausgeführt. Dies bedeutet, dass Sie Folgendes tun können:

try {
  // Do processing
  return result;
} finally {
  // Release resources
}

Dadurch werden die Ressourcen unabhängig davon freigegeben, wie die Methode mit einer Ausnahme oder einer regulären Rückgabeanweisung beendet wurde.

Ob dies gut oder schlecht ist, steht zur Debatte, ist aber try {} finally {}nicht immer auf die Ausnahmebehandlung beschränkt.

Kamiel Wanrooij
quelle
0

Ich könnte der Zorn Pythonistas aufrufen (weiß nicht, wie ich Python nicht viel verwenden) oder Programmierer aus anderen Sprachen mit dieser Antwort, aber meiner Meinung nach die meisten Funktionen sollten nicht einen haben catchBlock, ideal gesprochen. Um zu zeigen, warum, lassen Sie mich dies mit der manuellen Weitergabe von Fehlercodes vergleichen, wie ich sie bei der Arbeit mit Turbo C Ende der 80er und Anfang der 90er Jahre ausführen musste.

Nehmen wir also an, wir haben eine Funktion zum Laden eines Bildes oder dergleichen, wenn ein Benutzer eine zu ladende Bilddatei auswählt, und dies ist in C und Assembly geschrieben:

Bildbeschreibung hier eingeben

Ich habe einige Funktionen auf niedriger Ebene weggelassen, aber wir können sehen, dass ich verschiedene Kategorien von Funktionen identifiziert habe, die farbcodiert sind, basierend auf den Verantwortlichkeiten, die sie in Bezug auf die Fehlerbehandlung haben.

Fehlerursache und Wiederherstellung

Nun war es nie schwer, die Kategorien von Funktionen zu schreiben, die ich als "mögliche throwFehlerquellen " (die, die das sind) und die "Fehlerbehebungs- und Berichterstellungsfunktionen" (die, die das catchsind) bezeichne.

Es war immer trivial, diese Funktionen korrekt zu schreiben, bevor die Ausnahmebehandlung verfügbar war, da eine Funktion, die auf einen externen Fehler stoßen kann, wie z. B. die Nichtzuweisung von Speicher, einfach einen NULLoder 0oder -1oder einen globalen Fehlercode oder etwas in diesem Sinne zurückgeben kann. Und die Fehlerbehebung / -meldung war immer einfach, da Sie, nachdem Sie sich den Call-Stack bis zu einem Punkt durchgearbeitet haben, an dem es sinnvoll war, Fehler zu beheben und zu melden, einfach den Fehlercode und / oder die Meldung nehmen und dem Benutzer melden. Und natürlich ist eine Funktion am Blatt dieser Hierarchie, die niemals ausfallen kann, egal wie sie in Zukunft geändert wird ( Convert Pixel), kinderleicht zu schreiben (zumindest in Bezug auf die Fehlerbehandlung).

Fehlerausbreitung

Die mühsamen Funktionen, die für menschliches Versagen anfällig sind, sind die Fehlerausbreitungsprogramme , die nicht direkt auf Fehler stoßen , sondern Funktionen aufrufen, die irgendwo in der Hierarchie versagen könnten. An diesem Punkt Allocate Scanlinekönnte einen Ausfall von handhaben müssen mallocund dann einen Fehler zurück nach unten zu Convert Scanlines, dann Convert Scanlineswürde für diesen Fehler überprüfen müssen und es weitergeben zu Decompress Image, dann Decompress Image->Parse Image, und Parse Image->Load Image, und Load Imageden Benutzer-End - Befehl , wo der Fehler schließlich gemeldet .

Hier machen viele Menschen Fehler, da nur ein Fehler-Propagator den Fehler nicht überprüft und weitergibt, damit die gesamte Funktionshierarchie zusammenbricht, wenn es darum geht, den Fehler richtig zu behandeln.

Wenn Fehlercodes von Funktionen zurückgegeben werden, verlieren wir in etwa 90% unserer Codebasis die Fähigkeit, bei Erfolg interessante Werte zurückzugeben, da so viele Funktionen ihren Rückgabewert für die Rückgabe eines Fehlercodes reservieren müssten Scheitern .

Reduzierung menschlicher Fehler: Globale Fehlercodes

Wie können wir also die Möglichkeit menschlicher Fehler reduzieren? Hier könnte ich sogar den Zorn einiger C-Programmierer hervorrufen, aber eine sofortige Verbesserung meiner Meinung nach ist die Verwendung globaler Fehlercodes wie OpenGL mit glGetError. Dies gibt den Funktionen zumindest die Möglichkeit, im Erfolgsfall aussagekräftige Werte von Interesse zurückzugeben. Es gibt Möglichkeiten, diesen Thread sicher und effizient zu gestalten, wenn der Fehlercode in einem Thread lokalisiert ist.

Es gibt auch Fälle, in denen eine Funktion möglicherweise auf einen Fehler stößt, es jedoch relativ harmlos ist, etwas länger zu warten, bevor sie aufgrund der Entdeckung eines früheren Fehlers vorzeitig zurückkehrt. Dies ermöglicht, dass so etwas passiert, ohne dass 90% der Funktionsaufrufe, die in jeder einzelnen Funktion ausgeführt werden, auf Fehler überprüft werden müssen, sodass eine ordnungsgemäße Fehlerbehandlung möglich ist, ohne dass dies so genau ist.

Reduzierung menschlicher Fehler: Ausnahmebehandlung

Die obige Lösung erfordert jedoch immer noch so viele Funktionen, um den Steuerflussaspekt der manuellen Fehlerausbreitung zu behandeln, selbst wenn sie die Anzahl der Zeilen des manuellen if error happened, return errorCodetyps verringert haben könnte . Es würde es nicht vollständig beseitigen, da es immer noch häufig mindestens eine Stelle geben müsste, die nach einem Fehler sucht und für fast jede einzelne Fehlerausbreitungsfunktion zurückkehrt. Dies ist also der Zeitpunkt, an dem die Ausnahmebehandlung ins Spiel kommt, um den Tag zu retten (sorta).

Der Wert der Ausnahmebehandlung besteht hier jedoch darin, den Kontrollflussaspekt der manuellen Fehlerausbreitung nicht mehr zu berücksichtigen. Dies bedeutet, dass sein Wert an die Fähigkeit gebunden ist, zu vermeiden, dass Sie catchin Ihrer gesamten Codebasis eine Bootsladung von Blöcken schreiben müssen . In der obigen Abbildung ist der einzige Ort, an dem ein catchBlock vorhanden sein muss, der Ort, an Load Image User Commanddem der Fehler gemeldet wird. Nichts anderes sollte idealerweise catchirgendetwas zu tun haben, da es sonst langsam so mühsam und fehleranfällig wird wie die Fehlercode-Behandlung.

Wenn Sie mich also fragen, ob Sie eine Codebasis haben, die auf elegante Weise wirklich von der Ausnahmebehandlung profitiert, sollte sie die Mindestanzahl von catchBlöcken haben (mit Minimum meine ich nicht Null, sondern eher eine für jeden einzelnen High-End-Code). Endbenutzeroperation, die fehlschlagen könnte, und möglicherweise noch weniger, wenn alle High-End-Benutzeroperationen über ein zentrales Befehlssystem aufgerufen werden).

Ressourcenbereinigung

Die Ausnahmebehandlung beseitigt jedoch nur die Notwendigkeit, die Steuerflussaspekte der Fehlerausbreitung nicht manuell auf Pfaden zu behandeln, die von normalen Ausführungsabläufen getrennt sind. Oft kann eine Funktion, die als Fehler-Propagator dient, auch dann noch Ressourcen beschaffen, wenn sie dies jetzt automatisch mit EH tut. Zum Beispiel kann eine solche Funktion eine temporäre Datei öffnen, die sie schließen muss, bevor sie von der Funktion zurückkehrt, egal was passiert, oder einen Mutex sperren, den sie entsperren muss, egal was passiert.

Dafür könnte ich den Zorn vieler Programmierer aus allen möglichen Sprachen hervorrufen, aber ich halte den C ++ - Ansatz für ideal. Die Sprache führt Destruktoren ein, die deterministisch aufgerufen werden, sobald ein Objekt den Gültigkeitsbereich verlässt. Aus diesem Grund muss C ++ - Code, der z. B. einen Mutex durch ein Mutexobjekt mit Gültigkeitsbereich mit einem Destruktor sperrt, diesen nicht manuell entsperren, da er automatisch entsperrt wird, sobald das Objekt den Gültigkeitsbereich verlässt, unabhängig davon, was passiert (auch wenn eine Ausnahme vorliegt) angetroffen). Es ist also wirklich kein gut geschriebener C ++ - Code erforderlich, um jemals mit der Bereinigung lokaler Ressourcen fertig zu werden.

In Sprachen ohne Destruktoren müssen sie möglicherweise einen finallyBlock verwenden, um lokale Ressourcen manuell zu bereinigen. Das heißt, es ist immer noch besser, Ihren Code mit manueller Fehlerausbreitung zu verunreinigen, vorausgesetzt, Sie müssen nicht catchüberall Ausnahmen machen.

Umkehrung äußerer Nebenwirkungen

Dies ist das am schwierigsten zu lösende konzeptionelle Problem. Wenn eine Funktion, unabhängig davon, ob es sich um einen Fehlerfortpflanzer oder einen Fehlerort handelt, externe Nebenwirkungen verursacht, muss sie diese Nebenwirkungen zurücksetzen oder "rückgängig machen", damit das System wieder in einen Zustand versetzt wird, als ob der Vorgang niemals statt eines " half-valid "Zustand, in dem die Operation zur Hälfte erfolgreich war. Ich kenne keine Sprachen, die dieses konzeptionelle Problem viel einfacher machen, mit Ausnahme von Sprachen, die lediglich die Notwendigkeit reduzieren, dass die meisten Funktionen externe Nebenwirkungen verursachen, wie funktionale Sprachen, die sich um Unveränderlichkeit und beständige Datenstrukturen drehen.

Dies finallyist wohl die eleganteste Lösung für das Problem in Sprachen, die sich um Veränderbarkeit und Nebenwirkungen drehen, da diese Art von Logik häufig sehr spezifisch für eine bestimmte Funktion ist und sich nicht so gut auf das Konzept der "Ressourcenbereinigung" abbilden lässt ". Und ich empfehle, finallyin diesen Fällen großzügig zu verwenden, um sicherzustellen, dass Ihre Funktion Nebenwirkungen in Sprachen umkehrt, die sie unterstützen, unabhängig davon, ob Sie einen catchBlock benötigen oder nicht (und wenn Sie mich fragen, sollte gut geschriebener Code die Mindestanzahl von haben catchBlöcke, und alle catchBlöcke sollten sich an Stellen befinden, an denen dies am sinnvollsten ist, wie im obigen Diagramm in Load Image User Command).

Traumsprache

IMO finallyist jedoch nahezu ideal für die Umkehrung von Nebenwirkungen, jedoch nicht ganz. Wir müssen eine booleanVariable einführen , um Nebenwirkungen im Falle eines vorzeitigen Abbruchs (von einer ausgelösten Ausnahme oder auf andere Weise) effektiv rückgängig zu machen:

bool finished = false;
try
{
    // Cause external side effects.
    ...

    // Indicate that all the external side effects were
    // made successfully.
    finished = true; 
}
finally
{
    // If the function prematurely exited before finishing
    // causing all of its side effects, whether as a result of
    // an early 'return' statement or an exception, undo the
    // side effects.
    if (!finished)
    {
        // Undo side effects.
        ...
    }
}

Wenn ich jemals eine Sprache entwerfen könnte, wäre mein Traum, dieses Problem zu lösen, die Automatisierung des obigen Codes:

transaction
{
    // Cause external side effects.
    ...
}
rollback
{
    // This block is only executed if the above 'transaction'
    // block didn't reach its end, either as a result of a premature
    // 'return' or an exception.

    // Undo side effects.
    ...
}

... mit Destruktoren, um die Bereinigung lokaler Ressourcen zu automatisieren, sodass wir sie nur benötigen transaction, rollbackund catch(obwohl ich vielleicht noch hinzufügen möchte, wenn finallywir beispielsweise mit C-Ressourcen arbeiten, die sich nicht selbst bereinigen). Allerdings finallymit einer booleanVariable ist die nächste Sache , dies einfach zu machen , dass ich gefunden habe , so weit meine Traumsprache fehlt. Die zweitkomplizierteste Lösung, die ich dafür gefunden habe, sind Scope Guards in Sprachen wie C ++ und D, aber ich fand Scope Guards konzeptionell immer etwas umständlich, da sie die Idee der "Ressourcenbereinigung" und "Umkehrung von Nebenwirkungen" verwischen. Meiner Meinung nach sind das sehr unterschiedliche Ideen, die auf andere Weise angegangen werden müssen.

Mein kleiner Wunschtraum einer Sprache würde sich auch stark um Unveränderlichkeit und dauerhafte Datenstrukturen drehen, um das Schreiben effizienter Funktionen, die keine umfangreichen Datenstrukturen in ihrer Gesamtheit kopieren müssen, obwohl die Funktion dies bewirkt, zu vereinfachen, obwohl dies nicht erforderlich ist keine Nebenwirkungen.

Fazit

Abgesehen von meinen Ramblings finde ich, dass Ihr try/finallyCode zum Schließen des Sockets in Ordnung und großartig ist, wenn man bedenkt, dass Python nicht das C ++ - Äquivalent von Destruktoren hat, und ich persönlich denke, dass Sie das großzügig für Orte verwenden sollten, die Nebenwirkungen umkehren müssen und minimieren Sie die Anzahl der Stellen, an denen Sie arbeiten müssen, catchzu Stellen, an denen dies am sinnvollsten ist.


quelle
-66

Es wird dringend empfohlen, Fehler / Ausnahmen abzufangen und ordnungsgemäß zu behandeln, auch wenn dies nicht vorgeschrieben ist.

Der Grund, warum ich das sage, ist, dass ich glaube, dass jeder Entwickler das Verhalten seiner Anwendung kennen und angehen sollte, andernfalls hat er seine Arbeit nicht ordnungsgemäß abgeschlossen. Es gibt keine Situation, in der ein try-finally-Block den try-catch-finally-Block ersetzt.

Ich gebe Ihnen ein einfaches Beispiel: Angenommen, Sie haben den Code zum Hochladen von Dateien auf den Server geschrieben, ohne Ausnahmen abzufangen. Wenn der Upload aus irgendeinem Grund fehlschlägt, weiß der Client nie, was schief gelaufen ist. Wenn Sie die Ausnahme abgefangen haben, können Sie eine ordentliche Fehlermeldung anzeigen, in der erklärt wird, was schief gelaufen ist und wie der Benutzer Abhilfe schaffen kann.

Goldene Regel: Ausnahme immer abfangen, da das Erraten Zeit braucht

Pankaj Upadhyay
quelle
22
-1: In Java kann eine finally-Klausel erforderlich sein, um Ressourcen freizugeben (z. B. eine Datei zu schließen oder eine DB-Verbindung freizugeben). Das ist unabhängig von der Fähigkeit, eine Ausnahme zu behandeln.
Kevin Cline
@ Kevincline, er fragt nicht, ob er endlich verwenden soll oder nicht ... Alles, was er fragt, ist, ob das Abfangen einer Ausnahme erforderlich ist oder nicht .... er weiß, was er versucht, abfängt und schließlich tut ..... Endlich ist das Wichtigster Teil, wir alle wissen, dass und warum es verwendet wird ....
Pankaj Upadhyay
63
@Pankaj: Ihre Antwort schlägt vor, dass eine catchKlausel immer vorhanden sein sollte, wenn es eine gibt try. Erfahrene Autoren, einschließlich mir, glauben, dass dies ein schlechter Rat ist. Ihre Argumentation ist fehlerhaft. Die Methode, die das enthält, tryist nicht der einzige mögliche Ort, an dem die Ausnahme abgefangen werden kann. Es ist oft am einfachsten und besten, Ausnahmen von der obersten Ebene abzufangen und zu melden, anstatt Fangklauseln im gesamten Code zu duplizieren.
Kevin Cline
@ Kevincline, ich glaube, die Wahrnehmung der Frage auf anderen Teil ist etwas anders. Die Frage lautete genau: Sollten wir eine Ausnahme machen oder nicht? Und ich antwortete in dieser Hinsicht. Die Behandlung von Ausnahmefällen kann auf verschiedene Arten erfolgen und ist nicht nur ein endgültiger Versuch. Dies war jedoch nicht das Anliegen von OP. Wenn es für Sie und andere gut genug ist, eine Ausnahme auszulösen, dann muss ich viel Glück sagen.
Pankaj Upadhyay
Mit Ausnahmen möchten Sie, dass die normale Ausführung von Anweisungen unterbrochen wird (und nicht bei jedem Schritt manuell auf Erfolg überprüft wird). Dies ist insbesondere bei tief verschachtelten Methodenaufrufen der Fall. Eine Methode 4-Ebene in einer Bibliothek kann eine Ausnahme nicht einfach "schlucken". es muss durch alle Schichten zurückgeworfen werden. Natürlich kann jede Ebene die Ausnahme einschließen und zusätzliche Informationen hinzufügen. Dies wird häufig aus verschiedenen Gründen nicht durchgeführt, da zusätzliche Entwicklungszeit und unnötige Ausführlichkeit die beiden größten sind.
Daniel B