Ist es jemals in Ordnung, eine leere catch-Anweisung zu haben?

58

Ich habe darüber nachgedacht und konnte mir kein Beispiel einfallen lassen. Warum sollte jemand eine Ausnahme erwischen und nichts dagegen unternehmen wollen? Kannst du ein Beispiel geben? Vielleicht ist es nur etwas, was man niemals tun sollte.

ysolik
quelle
Was ist mit endlich?
Chris
2
btw - dies wurde am SO letzten Jahr gefragt: stackoverflow.com/questions/1234343/...
warren
17
Es sollte mindestens einen Kommentar enthalten, warum es leer ist.
Peterchen

Antworten:

77

Ich mache es die ganze Zeit mit Dingen wie Konvertierungsfehlern in D:

import std.conv, std.stdio, std.exception;

void main(string[] args) {  
    enforce(args.length > 1, "Usage:  foo.exe filename");

    double[] nums;

    // Process a text file with one number per line into an array of doubles,
    // ignoring any malformed lines.
    foreach(line; File(args[1]).byLine()) {
        try {
            nums ~= to!double(line);
        } catch(ConvError) {
            // Ignore malformed lines.
        }
    }

    // Do stuff with nums.
}

Das heißt, ich denke, dass alle catch-Blöcke etwas enthalten sollten, auch wenn dies nur ein Kommentar ist, der erklärt, warum Sie die Ausnahme ignorieren.

Bearbeiten: Ich möchte auch betonen, dass Sie, wenn Sie so etwas tun, darauf achten sollten, nur die bestimmte Ausnahme zu erfassen, die Sie ignorieren möchten. Ein einfaches altes zu tun catch {}ist fast immer eine schlechte Sache.

dsimcha
quelle
15
+1 für Erklärungskommentar.
Michael K
In einigen IDEs können Sie angeben, dass ein bestimmter Name für die Ausnahme (normalerweise "ignorieren") darauf hinweist, dass Sie sie absichtlich ignorieren, wodurch die Warnung unterdrückt wird, die sie sonst anzeigen würde. dies ersetzt den Kommentar.
Alex Feinman
Ich kenne D nicht, aber ich gehe davon aus, dass es möglich ist, so etwas wie bool TryParse (string line, out double valToParse) zu machen, was sauberer sein kann.
Ysolik
4
Wow, jemand, der tatsächlich D benutzt! :-P
James McNellis
4
Wie @Michael sagt - es ist nicht ganz leer, da Sie den Kommentar dort haben; Es sollte obligatorisch sein, einen Kommentar "dieser Fang absichtlich leer gelassen" hinzuzufügen, wenn es keine Aussage gibt
STW
50

Ein Beispiel, bei dem ich denke, dass es in Ordnung ist, eine Ausnahme nur zu verschlucken, ohne etwas zu tun, selbst wenn die Ausnahme protokolliert wird, ist der Protokollierungscode selbst.

Wenn Sie versucht haben, etwas zu protokollieren und eine Ausnahme festgestellt haben, können Sie nicht viel dagegen tun:

  • du kannst es natürlich nicht anmelden;
  • Möglicherweise können Sie auf einen Reserveaufzeichnungsmechanismus zurückgreifen, aber die meisten Anwendungen sind nicht so ausgefeilt, und für diejenigen, die ausgefeilt genug sind, tritt das gleiche Entwurfsproblem mit dem Reserveaufzeichnungsmechanismus auf.
  • schnelles Versagen - wird für die meisten Anwendungen zu radikal sein;
  • Exception-Up zu werfen bringt wenig, da es zu einer catch-Anweisung auf dem Stack führt, die fast sicher versucht, sie zu protokollieren ...

So haben Sie die Möglichkeit, "das Geringste vom Bösen" im Hintergrund zu schlucken, sodass die Anwendung weiterhin ihre Hauptaufgabe erfüllen kann, ohne jedoch die Möglichkeit zur Fehlerbehebung zu beeinträchtigen.

kdubinets
quelle
10
Toller Punkt. Das Debuggen des Debugging-Codes ist immer eine Herausforderung.
DevSolo
7
Das ist eine Million Mal. Vor allem, wenn Sie bedenken, dass Sie sich bei der Protokollierung in einem schrecklichen Zustand befinden. Sie könnten nicht mehr im Gedächtnis sein, könnten abstürzen, könnten alles sein. Zu diesem Zeitpunkt, wenn Sie einen Fehler protokollieren und dieser fehlschlägt, ist das einzig Verantwortliche, was zu tun ist, nichts. Sie können nicht garantieren, dass alles, was Sie versuchen, es nicht noch schlimmer macht.
GWLlosa
Großartiger Punkt. In meinem Protokollierungscode möchte ich auch die InnerMessage für die Ausnahme hinzufügen. Oft ist dies null. Wenn Sie also versuchen, es hinzuzufügen, wird die Protokollierung selbst in die Luft gejagt.
Tangurena
@Tangurena Wenn es oft null ist, dann mach das, blase nicht. In C # bewache ich solche Dinge oft durch ?? "";
Loren Pechtel
8

Wenn eine Ausnahme von einer Operation ausgelöst wird, die ohnehin optional war, ist möglicherweise nichts zu tun. Es ist möglicherweise nicht sinnvoll, es zu protokollieren. In diesem Fall möchten Sie es vielleicht nur fangen und nichts tun.

Fügen Sie in diesem Fall einen Kommentar hinzu. Kommentieren Sie immer alles, was wie ein Fehler aussieht (Durchfall in einer switchAnweisung, leerer catchBlock, Zuweisung in einer Bedingung).

Sie könnten argumentieren, dass dies keine ordnungsgemäße Verwendung von Ausnahmen ist, aber das ist eine andere Frage.

David Thornley
quelle
6

Leere catchBlöcke riechen in den meisten Sprachen nach Code. Die Hauptidee ist, Ausnahmen für Ausnahmesituationen zu verwenden und sie nicht für die logische Steuerung zu verwenden. Alle Ausnahmen müssen irgendwo behandelt werden.

Infolge dieser Vorgehensweise haben Sie beim Programmieren einer bestimmten Anwendungsebene mehrere Möglichkeiten:

  • Tun Sie nichts gegen die Ausnahme. Es wird von einer anderen Schicht gefangen und gehandhabt
  • fangen Sie es und führen Sie die Korrekturmaßnahme durch.
  • Fang es, mach etwas, aber wirf es erneut, damit eine andere Schicht damit fertig wird

Das lässt eigentlich keinen Raum für nichts zu tun, leere catchBlöcke.

EDITIERT ZUM HINZUFÜGEN : Angenommen, Sie programmieren in einer Sprache, in der das Auslösen von Ausnahmen die normale Methode zum Steuern der Programmlogik ist (eine der Alternativen zu goto). Dann ist ein leerer catchBlock in einem Programm, das in dieser Sprache geschrieben ist, einem leeren elseBlock in einer traditionellen Sprache (oder überhaupt keinem elseBlock) sehr ähnlich . Ich glaube jedoch, dass dies nicht der von C # -, Java- oder C ++ - Entwicklergemeinschaften empfohlene Programmierstil ist.

Azheglov
quelle
Ich denke, dass die Verwendung von Ausnahmen als logische Steuerung ziemlich verbreitet ist, daher bin ich ziemlich oft mit leeren oder fast leeren Fangblöcken konfrontiert.
Whatsisname
+1 für das Erkennen, dass ein leerer catch-Block die Verwendung von Ausnahmen für die Ablaufsteuerung anzeigen kann.
John M Gant
Das Verwenden von Ausnahmen für die Programmierlogik wird im Allgemeinen als schlechte Praxis angesehen. Aber überraschenderweise (mein ursprüngliches Argument gegen Ausnahmen) verursachen Ausnahmen keinen spürbaren Leistungseinbruch. Siehe codeproject.com/KB/exception/ExceptionPerformance.aspx .
Evan Plaice
6

In einigen Fällen müssen Sie in Java eine Ausnahme behandeln, die unter keinen Umständen jemals auftreten kann. Betrachten Sie diesen Code:

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

Jede Implementierung der Java-Plattform ist erforderlich, um die folgenden Standardzeichensätze zu unterstützen.

...

UTF-8

Ohne Ihre Java-Plattform zu beschädigen, gibt es einfach keine Möglichkeit, diese Ausnahme jemals auszulösen. Warum also damit umgehen? Sie können die try-catch-Klausel jedoch nicht einfach weglassen.

user281377
quelle
1
In solchen Fällen wäre ich versucht, etwas hinzuzufügen throw new Error(e), um absolut sicherzugehen, dass ich nichts verpasst habe (oder tatsächlich kaputte Plattform melde).
Halle Knast
@HalleKnast Ja. Dies wurde hier ausführlich besprochen: softwareengineering.stackexchange.com/questions/122233/…
user281377
5

In der Regel nein. Sie möchten entweder die Ausnahme auf den Stapel werfen oder protokollieren, was passiert ist.

Ich mache dies jedoch oft, wenn ich Zeichenfolgen analysiere und in Zahlen konvertiere (insbesondere bei Mapping-Projekten, die nur einmal verwendet werden können und Abwechslung bieten).

boolean isNonZeroNumber = false;

try {
   if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
     b = true;
   }
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}

return b;
Fil
quelle
3

In einigen Fällen ist die Ausnahme überhaupt nicht außergewöhnlich. in der Tat wird es erwartet. Betrachten Sie dieses Beispiel:

    /* Check if the process is still alive; exitValue() throws an exception if it is */
    try {
        p.exitValue();
        processPool.remove(p);
    }
    catch (IllegalThreadStateException e) { /* expected behaviour */ }

Da es in java.lang.Process () keine isAlive () -Methode gibt, können Sie nur mit exitValue () überprüfen, ob der Prozess noch aktiv ist. Dabei wird eine IllegalThreadStateException ausgelöst, wenn der Prozess noch ausgeführt wird.

user281377
quelle
2

Nach meiner bescheidenen Erfahrung ist das selten gut. Ihr Kilometerstand kann jedoch variieren.

Fast jedes Mal, wenn ich dies in unserer Codebasis gesehen habe, ist dies darauf zurückzuführen, dass ich einen Fehler gefunden habe, den die verzehrte Ausnahme ausgeblendet hat. Anders ausgedrückt: Wenn Sie keinen Code bereitstellen, kann die Anwendung keinen Fehler anzeigen oder eine andere Aktion ausführen.

Manchmal möchten oder können Sie beispielsweise auf API-Ebenen möglicherweise keine Ausnahmen zurücklassen. In diesem Fall tendiere ich dazu, die allgemeinsten zu ermitteln und diese dann zu protokollieren. Nochmals, um zumindest einer Debug- / Diagnosesitzung eine Chance zu geben.

Wenn es leer sein muss , stelle ich in der Regel zumindest eine Behauptung auf, sodass zumindest während einer Debugsitzung etwas passiert. Das heißt, wenn ich nicht damit umgehen kann, neige ich dazu, sie komplett wegzulassen.

DevSolo
quelle
2

IMO gibt es eine Stelle, an der leere catch-Anweisungen in Ordnung sind. In Testfällen, in denen Sie eine Ausnahme erwarten:

try
{
   DoSomething(); //This should throw exception if everything works well
   Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
    //Expected to get here
}
Victor Hurdugaci
quelle
+1, ich hasse es, dass ich das tue, aber es funktioniert in JUnit
sal
@sal: was ist mit @Test (erwartet = Exception.class)?
Ysolik
@ysolik, schlechte Angewohnheit
sal
1
@sal: Warum ist das eine schlechte Angewohnheit?
Adam Lear
ummmm. Es gibt Möglichkeiten, dies mit Unit-Testing-Frameworks zu tun. Ex. NUnit. Es gibt bestimmte Fälle, in denen es nützlich sein kann, zu testen, ob etwas vorhersehbar fehlschlägt. Obwohl ich es niemals im Seriencode machen würde.
Evan Plaice
2

Ich glaube, es gibt bestimmte Fälle, in denen es gerechtfertigt ist, eine leere catch-Klausel zu haben - dies sind Fälle, in denen der Code einfach weiterarbeiten soll, wenn eine bestimmte Operation fehlschlägt. Ich bin jedoch der Meinung, dass dieses Muster leicht überbeansprucht werden kann. Daher sollte jede Verwendung genau untersucht werden, um sicherzustellen, dass es keinen besseren Weg gibt, damit umzugehen. Dies sollte insbesondere dann nicht erfolgen, wenn das Programm in einem inkonsistenten Zustand bleibt oder wenn später ein anderer Teil des Codes ausfällt.

O. nate
quelle
1

Ich glaube nicht, dass ich im Ausnahmefall keine Alarmstufe habe. Sollte eines der Ziele der Programmierung nicht darin bestehen, den Code zu verbessern? Wenn in 10% der Fälle eine Ausnahme auftritt und Sie nichts davon wissen, ist die Ausnahme immer so schlimm oder schlimmer.

Ich sehe jedoch die andere Seite: Wenn die Ausnahme bei der Codeverarbeitung keinen wirklichen Schaden anrichtet, warum sollte der Benutzer dann der Ausnahme ausgesetzt werden? Die Lösung, die ich normalerweise implementiere, besteht darin, sie von meiner Logger-Klasse protokollieren zu lassen (das ist in jeder einzelnen Klasse, die ich jemals bei der Arbeit schreibe).

Wenn es eine harmlose Ausnahme ist, rufe ich die .Debug () -Methode meines Loggers auf. ansonsten Logger.Error () (und (vielleicht) werfen).

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

Übrigens wird ein Aufruf der .Debug () -Methode in meiner Implementierung des Loggers nur protokolliert, wenn in meiner App.Config-Datei angegeben ist, dass sich die Programmausführungsprotokollebene im Debug-Modus befindet.

Tim Claason
quelle
Was ist, wenn _log.Debug eine Ausnahme auslöst (aus welchem ​​Grund auch immer)?
JohnL
Dann isst .Debug () die Ausnahme und ich weiß nie davon. Aber ich fühle mich in Bezug auf meinen Logger viel sicherer als in Bezug auf Code, der häufig Ausnahmen auslöst. Wenn ich mir darüber Sorgen machen würde, könnte ich vermutlich eine Art Muster implementieren, das auf den Eintritt in den catch-Block achtet und es für später speichert. Auf jeden Fall - Punkt gemacht
Tim Claason
1

Existenzprüfung ist ein guter Anwendungsfall:

// If an exception happens, it doesn't matter. Log the initial value or the new value

function logId(person) {
    let id = 'No ID';
    try {
        id = person.data.id;
    } catch {}
    console.log(id);
}

Ein anderer Fall ist, wenn eine finallyKlausel verwendet wird:

 function getter(obj){}

 function objChecker()
   {
   try
     {
     /* Check argument length and constructor type */
     if (getter.length === 1 && getter.constructor === Function)
       {
       console.log("Correct implementation");
       return true;
       }
     else
       {
       console.log("Wrong implementation");
       return false;
       }
     }
   catch(e)
     {
     }
   finally
     {
     console.log(JSON.stringify(getter))
     }
   }

 // Test the override of the getter method 
 getter = getter;
 objChecker();
 getter = [];
 objChecker();
 getter = {};
 objChecker();
 getter = RegExp;
 objChecker();
 getter = Function;
 objChecker();

Es gibt einen Vorschlag, das Weglassen der Fangbindung in ECMAScript zuzulassen, der sich auf diesen und ähnliche Anwendungsfälle bezieht.

Verweise

Paul Sweatte
quelle
Ein weiteres Beispiel: In nodejs wird die Methode fs.exists (die true oder false zurückgibt ) nicht mehr empfohlen ( nodejs.org/dist/latest-v10.x/docs/api/… ), da fs.access eine Ausnahme auslöst In diesem Fall existiert keine Datei. Wenn man nur etwas machen will, wenn die Datei existiert, ist die catch-Klausel völlig unnötig.
Systemovich
0

Das einzige Mal, dass ich so etwas wirklich tun musste, war eine Protokollierungsklasse für eine Konsolen-App. Es gab einen globalen Catch-Handler der obersten Ebene, der den Fehler an STDOUT sendete, den Fehler in einer Datei protokollierte und die App mit einem Fehlercode> 0 schloss.

Das Problem hierbei war, dass manchmal die Protokollierung in der Datei fehlschlug. Die Verzeichnisberechtigungen haben Sie daran gehindert, die Datei zu schreiben. Die Datei wurde unabhängig davon exklusiv gesperrt. In diesem Fall konnte ich die Ausnahmebedingung nicht wie an anderer Stelle behandeln (abfangen, relevante Details protokollieren , einen besseren Ausnahmetyp einbinden usw.), da das Protokollieren der Ausnahmedetails dazu führte, dass der Logger dieselben Aktionen ausführte ( Versuch, in eine Datei zu schreiben), die bereits fehlgeschlagen war. Als sie wieder versagten ... Sie sehen, wohin ich gehe. Es gerät in eine Endlosschleife.

Wenn Sie einige der try {} -Blöcke löschen, wird möglicherweise eine Ausnahme in einem Meldungsfeld angezeigt (nicht das, was Sie für eine stille Konfigurations-App wünschen). Bei dieser Methode, die in die Protokolldatei schreibt, habe ich einen leeren Catch-Handler. Dies begräbt den Fehler nicht, da Sie immer noch den Fehlercode> 0 erhalten. Es stoppt lediglich die Endlosschleife und ermöglicht es der App, ordnungsgemäß zu beenden.

Es gibt natürlich einen Kommentar, der erklärt, warum es leer ist.

(Ich wollte dies früher posten, ich hatte nur Angst, in Vergessenheit zu geraten. Da ich aber nicht der einzige bin ...)

JohnL
quelle
0

Es ist in einer Situation in Ordnung, in der eine Sequenz ausgeführt werden muss, unabhängig davon, welches der kritischsten Teile am Ende steht.

Zum Beispiel. In Motion Control Kommunikation von Host zu Controller. In Notsituationen gibt es eine Reihe von Vorbereitungsschritten, um die Bewegung abzubrechen, die Trajektorienerzeugung zu stoppen, die Motoren abzubremsen, die Pausen zu aktivieren, den Verstärker zu deaktivieren und danach die Busspannung zu unterbrechen. Alle Schritte müssen nacheinander ausgeführt werden. Wenn einer der Schritte fehlschlägt, müssen die restlichen Schritte trotzdem ausgeführt werden, auch wenn das Risiko einer Beschädigung der Hardware in Kauf genommen wird. Sagen wir, auch wenn alles oben genannte fehlschlägt, muss der Kill-Bus-Strom trotzdem passieren.

user7071
quelle
Das bedeutet nicht, dass Sie nichts tun, nur, dass das, was Sie tun, den Fluss nicht abbricht. Fangen Sie Ihre Ausnahmen ab, speichern Sie sie im Speicher (keine Verzögerung beim Schreiben von Datenträgern), und beenden Sie die Stromzufuhr in einer finally-Anweisung. Dann machen Sie mit den gespeicherten Informationen, was Sie wollen.
Loren Pechtel
0

Systemressourcen ordnungsgemäß schließen, um einen Fehler zu beheben

Zum Beispiel. Wenn Sie einen Dienst im Hintergrund ausführen, der dem Benutzer kein Feedback liefert, möchten Sie möglicherweise keine Fehlermeldung an den Benutzer senden , müssen jedoch die verwendeten Ressourcen ordnungsgemäß schließen.

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

Idealerweise verwenden Sie den Abfangmodus im Debug-Modus, um Fehler abzufangen und sie zum Testen an die Konsole oder in eine Protokolldatei zu drucken. In einer Produktionsumgebung wird allgemein erwartet, dass Hintergrunddienste den Benutzer nicht unterbrechen, wenn ein Fehler ausgegeben wird, sodass die Fehlerausgabe unterdrückt werden sollte.

Evan Scholle
quelle