Ausnahmen von Rückgabecodes: Verlieren wir etwas (während wir etwas anderes gewinnen)?

8

Meine Frage ist ziemlich vage: o) - Aber hier ist ein Beispiel:

Als ich C-Code schrieb, konnte ich den Wert des Zählers protokollieren, wenn etwas fehlschlug:

%Vor%

Jetzt, mit Ausnahmen, bekomme ich das:

%Vor%

Natürlich kann man "i" außerhalb der try / catch-Anweisung deklarieren (und das tue ich gerade). Aber ich mag es nicht - ich mag Variablen dort deklarieren, wo sie gebraucht werden, nicht vorher.

Aber vielleicht verpasse ich hier etwas. Haben Sie eine elegante Lösung?

Vielen Dank im Voraus! Sylvain.

HINZUGEFÜGT: myCall () ist ein obskurer API-Aufruf - ich habe keine Ahnung, was er werfen kann. Außerdem kann ich natürlich einen Try / Catch-Block um jeden Aufruf hinzufügen, aber dann wäre es besser, Returncodes zu verwenden. Würde ich dann um wichtige Codezeilen herum eine Menge Rauschen hinzufügen?

    
SRO 14.01.2009, 21:01
quelle

15 Antworten

8

Wie wäre es mit:

%Vor%     
scottm 14.01.2009, 21:07
quelle
7

Ich mag den Ausdruck "jetzt, mit Ausnahmen ..." nicht.

Ausnahmen sind ein Werkzeug , das Sie verwenden, um es in Ihrer Programmierung zu verwenden. Wenn Sie denken, dass es die beste Option ist, verwenden Sie es, andernfalls nicht.

Ich folge einer persönlichen Regel, keine Ausnahmen zu werfen, die ich vermeiden kann, im internen Code zu werfen. Für eine API einer öffentlich verfügbaren DLL sollten Vorbedingungsprüfungen aktiviert bleiben und Ausnahmen auslösen, wenn sie fehlschlagen, ja; aber für interne Logik, ich selten (wenn überhaupt) Ausnahmen in meinem Design enthalten. Umgekehrt, wenn ich eine Funktion verwende, die dokumentiert, dass sie ausgelöst wird, wenn eine schlechte Situation eintritt, neige ich dazu, die Ausnahme sofort zu erfassen - es ist schließlich eine erwartete Ausnahme.

Wenn Sie denken, dass Ihre nicht-außergewöhnliche Alternative besser ist - bleiben Sie dabei!

    
Daniel Daranas 14.01.2009 21:20
quelle
7

Ich denke, Sie haben es falsch verstanden, und es ist nicht überraschend, wie viele andere Leute auch tun.

Ausnahmen werden nicht für den Programmablauf verwendet. Lies das nochmal, es ist wichtig.

Ausnahmen gibt es für die Fehler "whoo, das sollte nicht passieren", die Sie hoffentlich nie zur Laufzeit sehen werden. Offensichtlich werden Sie sie in dem Moment sehen, in dem Ihr erster Benutzer sie benutzt, weshalb Sie die Fälle berücksichtigen müssen, in denen sie passieren könnten. Sie sollten jedoch nicht versuchen, Code einzufügen, zu behandeln und fortzufahren, als ob nichts passiert wäre.

Für solche Fehler wollen Sie Fehlercodes. Wenn Sie Exceptions als Super-Fehlercodes verwenden, dann schreiben Sie Code, wie Sie es bereits erwähnt haben - jeden Methodenaufruf in einen try / catch-Block einbinden! Sie können auch ein enum zurückgeben, es ist viel viel schneller und wesentlich einfacher zu lesen Fehler Return Code als alles mit 7 Zeilen Code statt 1 (es ist auch wahrscheinlich, dass Code auch zu korrigieren) - Siehe erikkallens Antwort)

Nun, in der realen Welt ist es oft der Fall, dass Methoden Ausnahmen auslösen, die Sie lieber nicht hätten (z. B. EndOfFile). In diesem Fall müssen Sie den "try / catch wrapper" anti- Muster, aber wenn Sie Ihre Methoden zu entwerfen, verwenden Sie keine Ausnahmen für die tägliche Fehlerbehandlung - verwenden Sie sie nur für außergewöhnliche Umstände. (Ja, ich weiß, es ist schwierig, diese Art von Design richtig zu machen, aber das ist auch viel Designarbeit)

    
gbjbaanb 14.01.2009 22:58
quelle
5

Ja. 2 Dinge.

Setzen Sie die try-catch-Blöcke dort, wo sie sinnvoll sind. Wenn Sie an Ausnahmen von myCall interessiert sind (sowie den Wert von i), verwenden Sie

%Vor%

Objekte verschiedener Klassen für verschiedene Fehler werfen. Wenn Sie an logischen Fehlern in der Finanzverarbeitung interessiert sind, werfen Sie finances :: logic_error, nicht std :: exception ("error msg") oder etwas. Auf diese Weise können Sie fangen, was Sie brauchen.

    
Iraimbilanja 14.01.2009 21:09
quelle
4

Zwei Dinge hier, aus meiner Sicht.

Es ist nicht unverschämt zu erwarten, dass die Ausnahme selbst Informationen über den Wert von i enthält, oder weniger spezifisch über den Kontext, in dem sie ausgewertet wurde und was schiefgelaufen ist. Für ein triviales Beispiel würde ich niemals einfach eine direkte InvalidArgumentException werfen; Stattdessen würde ich sicherstellen, dass ich eine genaue Beschreibung in den Konstruktor eingegeben habe, z. B.

%Vor%

Dies könnte den Wert von i nicht explizit protokollieren, aber in den meisten Fällen werden Sie in der Lage sein zu verstehen, was das Problem mit Ihrer Eingabe war, die den Fehler verursacht hat. Dies ist ein Argument für die Ausnahmeverkettung - wenn Sie Ausnahmen auf jeder konzeptionellen Ebene fangen, umbrechen und erneut ausführen, kann jede Umhüllung ihre eigenen relevanten Variablen hinzufügen, die zu hoch sind, um von dem grundlegenden Fehler auf niedriger Ebene gesehen oder verstanden zu werden .

Wenn die Dinge für Ihre myCall -Funktion wirklich zu abstrakt sind, um zu wissen, was vor sich geht, dann finde ich, dass die Protokollierung auf einer höheren Ausführlichkeitsstufe erfolgt, bevor der Anruf gut funktioniert, z. B.

%Vor% Auf diese Weise können Sie, wenn etwas schief geht, Ihr Debugging-Protokoll mit hoher Ausführlichkeit überprüfen und herausfinden, was i gerade vor dem Aufruf war, der die Ausnahme ausgelöst hat.     
Andrzej Doyle 14.01.2009 23:16
quelle
4

Betrachten Sie die Meinungen von Raymond Chen sowie Microsofts x64-Denken.
((Raymond chen Ausnahmen)) als eine Google-Abfrage ist genug, um Sie zu seinen klassischen Essays "Cleaner, eleganter und falsch - Nur weil Sie nicht sehen, den Fehler Weg bedeutet nicht, dass es nicht existiert . " und die Klärung "Cleaner, eleganter und schwerer zu erkennen".
((x64 exception model)) bringt Sie zum MSDN-Artikel "Alles, was Sie wissen müssen, um 64-Bit-Windows-Systeme zu programmieren", welches das Zitat "Die Nachteile tabellenbasierter Ausnahmebehandlung (relativ zum x86 stack-based Modell) ist, dass das Nachschlagen von Funktionstabelleneinträgen von Codeadressen mehr Zeit in Anspruch nimmt, als nur eine verkettete Liste zu gehen. Der Vorteil ist, dass Funktionen nicht den Aufwand haben, einen Versuchsdatenblock jedes Mal zu erstellen, wenn die Funktion ausgeführt wird Um dieses Zitat zusammenzufassen, ist es in x64 frei oder fast-frei, einen "catch" einzurichten, der niemals verwendet wird, aber das Werfen einer Ausnahme ist langsamer als in x86.

    
pngaz 15.01.2009 06:25
quelle
3

Es kann eleganter sein, wenn das Objekt, das Sie werfen, die Kontextinformationen enthalten kann, die Ihnen etwas über die Art des Fehlers sagen.

Leite ein würfelbares Objekt aus einem istream ab und du kannst & gt; & gt; um Informationen hinein zu streamen. Bringen Sie dem Objekt bei, wie es sich selbst anzeigen soll & lt; & lt;.

Wenn Sie eine Fehlerbedingung in der darunter liegenden Ebene oder in den darunter liegenden N Ebenen erkennen. Füllen Sie Ihr Objekt mit guten Kontextinformationen aus und werfen Sie es ab. Wenn Sie das Objekt fangen, sagen Sie ihm, dass es seine Kontextinformationen in der Protokolldatei und / oder auf dem Bildschirm und / oder in der von Ihnen gewünschten Position anzeigen soll.

    
EvilTeach 14.01.2009 21:06
quelle
2
  

Natürlich kann man "i" außerhalb der try / catch-Anweisung deklarieren (und das mache ich gerade).

Nun ... wenn Sie wirklich den Wert von i kennen müssen, dann scheint dies ein Protokollierungsvehikel zu sein - strukturierte Ausnahmebehandlung ist wahrscheinlich nicht der beste Weg. Wenn Sie die Ausnahme bedingt behandeln möchten (d. H. Nur beim Debuggen), fügen Sie den try block innerhalb der Schleife ein. Da dies die Leistung beeinträchtigen kann (abhängig von Ihrer Umgebung), tun Sie dies nur im Debug-Modus.

    
Konrad Rudolph 14.01.2009 21:07
quelle
2

Erste Dinge zuerst. Wenn Sie Exception fangen, liegen Sie falsch. Sie sollten die spezifische Ausnahme abfangen, die Sie erwarten.

Aber abgesehen davon, wenn Ihre Ausnahmen von Ihrem eigenen Code ausgelöst werden, können Sie intelligente Ausnahmen verwenden, um alle Daten zu erfassen, die Sie über diese Ausnahme wissen müssen.

Zum Beispiel enthält eine ArrayIndexOutOfBoundsException den adressierten Index.

    
Matthew Brubaker 14.01.2009 21:08
quelle
2

Sie können spezifischere Informationen auf zwei Arten erhalten. Fangen Sie zuerst nicht Ausnahme, fangen Sie eine bestimmte Ausnahme ab. Zweitens, verwenden Sie mehrere try / catch-Anweisungen in einer Funktion, in der Sie sicher sein müssen, welcher Funktionsaufruf die Ausnahme ausgelöst hat.

    
Jon B 14.01.2009 21:09
quelle
2

Meiner Meinung nach sollten Ausnahmen in einem solchen Fall nicht verwendet werden, aber wenn Sie sie wirklich brauchen, würde ich den folgenden Weg gehen:

Sie könnten das 'i' als Parameter an myCall () übergeben; Funktion und wenn irgendein Fehler auftrat, würde irgendeine spezielle Ausnahme geworfen werden. Wie:

%Vor%

Der Schleifenblock:

%Vor%

Und schließlich die Funktion myCall ():

%Vor%     
arul 14.01.2009 21:13
quelle
2

Wir verlieren die Möglichkeit, leicht zu sehen, wie der Code Fehler an verschiedenen Stellen behandelt. Raymond Chen hat einen guten Artikel darüber geschrieben

    
erikkallen 14.01.2009 22:28
quelle
1

Gut! Du könntest das tun:

%Vor%

Ich denke Ausnahmen sind kühler als Java es dir zeigt. Was wirklich Spaß macht, ist, dass der Debugger jede nicht behandelte Exception aufruft und sich dann den Stack im Moment des Ausfalls ansieht, sodass Sie i untersuchen können, ohne eine Codezeile ändern zu müssen. Das heißt, ich glaube, für welche Ausnahmen es sich handeln sollte.

    
nes1983 14.01.2009 21:08
quelle
1

Viele Ausnahmebehandlungswerkzeuge ( MadExcept für Delphi ist eins) ermöglichen es Ihnen, den gesamten Stack-Trace abzurufen, wenn Sie die Ausnahme abfangen. Also, Sie würden genau wissen wo es geworfen wurde.

Die gängige Methode, um "i" zu erhalten, besteht darin, die Ausnahme abzufangen und ihr zusätzliche Daten hinzuzufügen (iStream-Technik), bevor sie erneut ausgelöst wird. Es ist selten, dass dies notwendig ist, aber wenn Sie darauf bestehen ...

    
Roddy 14.01.2009 21:13
quelle
-2

Sie können von RuntimeException ableiten und eine eigene Ausnahme erstellen, die mycall () auslöst. Dann können Sie das mit Versuch / fangen fangen.

    
mslot 14.01.2009 21:12
quelle

Tags und Links