Ausnahmen vs Spezielle Rückgabewerte

8

Welche ist eine bessere Übung in der Programmierung?

Ich spreche nicht von vollständiger Exklusivität. Es ist mehr für Dinge wie:

list<T>.Find , wobei Sie default(T) oder Ihren Wert anstelle von ValueNotFound exception (Beispiel) erhalten.

oder

list<T>.IndexOf , wobei Sie -1 oder den richtigen Index erhalten.

    
Joan Venge 23.03.2009, 17:14
quelle

15 Antworten

16

Nun, die Antwort hängt davon ab.

Wenn ein Element in einer Liste nicht gefunden wird, ist das Auslösen einer Ausnahme eine schreckliche Übung, da es durchaus möglich ist, dass das Element nicht in der Liste enthalten ist.

Wenn es sich nun um eine spezialisierte Liste von Art handelt und das Element absolut in der Liste enthalten sein sollte, sollte das eine Ausnahme auslösen, weil Sie eine Situation gefunden haben, die nicht machbar war / vernünftig.

Im Allgemeinen sind spezialisierte Fehlercodes für Dinge wie Geschäftsregeln und Ähnliches besser, weil Sie sich der Möglichkeit dieser Dinge bewusst sind und auf diese Möglichkeiten reagieren möchten. Ausnahmen gelten für die Fälle, die Sie nicht erwarten, und sie können nicht mit der Codeausführung fortgesetzt werden, wenn sie auftreten.

    
casperOne 23.03.2009 17:20
quelle
11

Ich habe irgendwo eine nette Regel darüber gelesen, die mir sehr gefällt. Es heißt - "eine Funktion sollte genau dann eine Ausnahme auslösen, wenn sie die Aufgabe, für die sie bestimmt war, nicht ausführen kann".

Was ich normalerweise tue, ist zu entscheiden, was eine Funktion tun soll (das kommt normalerweise von Geschäftsanforderungen oder etwas), und dann Ausnahmen für alles andere zu werfen.

Wenn Sie Ihre Anwendung gut entwickelt haben, sind Ihre Funktionen ziemlich klein und führen relativ einfache Aufgaben mit einfachen Rückgabewerten aus. Entscheidung über Ausnahmen durch die oben genannte Regel wird nicht schwierig sein.

Natürlich gibt es immer zweideutige Fälle (wie bei einem Schlüssel, der nicht in einem Wörterbuch gefunden wird). Diese sollten weit und dazwischen sein, aber dort müssen Sie nur Ihr Bauchgefühl verwenden, um die elegantere Lösung zu finden.

Ach, und dabei nie vergessen: Damit das gut funktioniert, fallen nur nette Ausnahmen, die man verarbeiten kann. Meistens bedeutet das, dass Sie sie nur in den oberen UI-Ebenen abfangen, in denen Sie dem Benutzer anzeigen und sie oder etwas aufzeichnen können. Niedrigere Ebenen können finally Blöcke verwenden oder Ausnahmen nach einer eigenen Verarbeitung erneut auslösen, aber echte abgefangene Ausnahmen in niedrigen Ebenen weisen normalerweise auf ein schlechtes Design hin.

    
Vilx- 23.03.2009 18:05
quelle
6

Eine Faustregel ist, Ausnahmen nur dann zu verwenden, wenn etwas passiert, was "nicht passieren sollte".

Wenn Sie erwarten, dass ein Aufruf von IndexOf () den fraglichen Wert nicht findet (eine vernünftige Erwartung), dann sollte es einen Rückgabecode dafür haben (wahrscheinlich -1, wie Sie sagen). Etwas, das niemals fehlschlagen sollte, wie das Zuweisen von Speicher, sollte in einem Fehlerfall eine Ausnahme auslösen.

Eine weitere Sache, an die Sie sich erinnern sollten, ist, dass die Handhabung von Ausnahmen in Bezug auf die Leistung "teuer" ist. Wenn Ihr Code also regelmäßig Ausnahmen im Rahmen normaler Operationen behandelt, wird er nicht so schnell wie möglich ausgeführt.

    
JeffH 23.03.2009 17:28
quelle
3

In den Fällen, die Sie erwähnt haben, würde ich den Rückgabewert bevorzugen, da ich genau weiß, was damit zu tun ist. Und es gibt keinen Grund, sich im gleichen Umfang mit Fängen zu befassen.

Natürlich, wenn Ihre Logik so aufgebaut ist, dass die Werte immer in der Liste sein sollten und ihre Abwesenheit ist der Programmierlogikfehler - Ausnahmen sind der Weg.

    
Mykola Golubyev 23.03.2009 17:20
quelle
3

Sie können meine zweiteilige Blogserie genießen, in der viele Kompromisse besprochen werden, je nachdem, welche Funktionen Ihre Programmiersprache unterstützt, da dies ziemlich relevant erscheint:

Ein Beispiel für das Zusammenspiel zwischen Sprachfunktionen und Bibliotheksdesign, Teil eins

Ein Beispiel für das Zusammenspiel zwischen Sprachfunktionen und Bibliotheksdesign, Teil zwei

Ich füge hinzu, dass viele der Antworten auf diese Frage schlecht sind (ich habe viele meiner Kohorten herabgestuft). Besonders schlecht sind APIs im Sinne von

%Vor%

die alle Arten von unglücklichen Problemen verursachen (siehe mein Blog für Details).

    
Brian 23.03.2009 17:39
quelle
3

Was würden Sie lieber verwenden?

A:

%Vor%

B:

%Vor%

C:

%Vor%

Ich wette, dass die Antwort A ist. Daher ist in diesem Fall die Rückgabe von Default (T) die richtige Wahl.

    
Jonathan Allen 23.03.2009 18:22
quelle
2

Bessere Sprachen lassen Sie alles tun, was Ihren Bedürfnissen entspricht. Wie in Smalltalk-80:

Folgendes wird eine Ausnahme auslösen, wenn kein Benutzer für die ID vorhanden ist:

%Vor%

Dieser wird den gegebenen Block auswerten, der eine High-Level-Funktion ist:

%Vor%

Bitte beachten Sie, dass die aktuelle Methode ist: ifAbsent :.

    
Squeaker 23.03.2009 19:15
quelle
1

Da ich von einem Java-Hintergrund komme, bevorzuge ich Ausnahmen in den meisten Fällen. Es macht Ihren Code viel sauberer, wenn die Hälfte davon keine Rückgabewerte überprüft.

Das heißt, es hängt auch von der Häufigkeit ab, dass etwas wahrscheinlich zu einem "Versagen" führt. Ausnahmen können teuer sein, so dass Sie sie nicht unnötigerweise für Dinge, die häufig versagen, herumwerfen.

    
Eric Petroelje 23.03.2009 17:21
quelle
1

Effektiver C # von Bill Wagner hat eine ausgezeichnete Empfehlung in Bezug auf Ausnahmen gemacht. Der Gedanke ist, eine Ausnahme zu machen, wenn eine Operation ausgeführt wird, stellen Sie sicher, dass Sie Haken bereitstellen, um zu überprüfen, ob der Wert eine Ausnahme auslöst.

Zum Beispiel

%Vor%

Auf diese Weise können Sie die Ausnahme deaktivieren, indem Sie etwas wie

schreiben %Vor%     
bendewey 23.03.2009 17:23
quelle
1

Wie bei vielen Problemen im Zusammenhang mit der Programmierung hängt alles davon ab ...

Ich finde, dass man zuerst einmal versuchen sollte, seine API so zu definieren, dass Ausnahmefälle gar nicht erst passieren können.

Die Verwendung von Design By Contract kann dabei helfen. Hier würde man Funktionen einfügen, die einen Fehler oder einen Absturz auslösen und einen Programmierfehler anzeigen (kein Benutzerfehler). (In einigen Fällen werden diese Prüfungen im Freigabemodus entfernt.)

Dann halten Sie Ausnahmen für generische Fehler, die nicht vermieden werden können, wie DB-Verbindung fehlgeschlagen, optimistische Transaktion fehlgeschlagen, Datenträger schreiben fehlgeschlagen.

Diese Ausnahmen sollten dann normalerweise nicht abgefangen werden, bis sie den "Benutzer" erreichen. Dies führt dazu, dass der Benutzer es erneut versuchen muss.

Wenn der Fehler ein Benutzerfehler wie ein Tippfehler in einem Namen oder etwas ist, dann behandeln Sie das direkt im Code der Anwendungsschnittstelle selbst. Da dies dann ein allgemeiner Fehler ist, müsste es mit einer benutzerfreundlichen Fehlermeldung, die möglicherweise übersetzt wurde, usw. behandelt werden.

Auch die Anwendungsschichtung ist hier nützlich. Nehmen wir als Beispiel das Überweisen von Bargeld von einem Konto auf ein anderes Konto:

%Vor%     
Jeroen Dirks 23.03.2009 17:50
quelle
0

Aus rein gestalterischer Sicht bevorzuge ich das Auslösen von Ausnahmen, wenn ein "außergewöhnliches" Ereignis eintritt, z. Der gesuchte Schlüssel wurde nicht gefunden. Der Hauptgrund ist, dass es den Konsumenten das Leben leichter macht - sie müssen den Wert nicht nach jedem Anruf auf Ihre Funktion überprüfen. Ich mag auch die TrySomething-Funktionen, denn dann kann der Benutzer explizit testen, ob die Operation erfolgreich war.

Leider sind Ausnahmen in .Net ziemlich teuer (aus meiner Erfahrung dauert es in der Regel etwa 50ms, um einen zu werfen), also sollten Sie aus praktischen Gründen einen dieser Standardwerte zurückgeben, anstatt eine Ausnahme auszulösen, besonders in der GUI-Programmierung.

    
Grzenio 23.03.2009 17:20
quelle
0

Ich denke, dass es etwas situationsbedingt ist, aber mehr davon abhängt, ob das Rückkehrereignis tatsächlich eine Ausnahme ist oder nicht. Das Beispiel indexOf ist eine vollkommen normale Ausgabeantwort, wobei -1 die einzige Sache ist, die Sinn macht (oder null für ref-Typen).

Eine Ausnahme sollte genau das sein: exceptional, was bedeutet, dass Sie alle Stack-Traces und die Handhabung, die Sie von einer echten Ausnahme erhalten können, wollen.

    
annakata 23.03.2009 17:20
quelle
0

Erstens mag ich die Option default(T) nicht wirklich: Was ist, wenn Sie eine int Liste haben, wo 0 ist (vermutlich ist das die int Standardeinstellung; ich benutze C # nicht) ist ein absolut zulässiger Wert?

(Entschuldigung für die Java-Syntax, aber wenn ich versuchen würde, C # zu erraten, würde ich das wahrscheinlich irgendwo durcheinander bringen :))

%Vor%

Dann würde Find natürlich ein Maybe<T> zurückgeben, und der Aufrufer wählt einen Wert, der in diesem Kontext ein vernünftiger Standard ist . Vielleicht könnten Sie getDefault als bequeme Methode hinzufügen, wenn default(T) ist, natürlich die richtige Antwort.

Für IndexOf ist jedoch -1 ein sinnvoller Wert, da gültige Werte offensichtlich immer & gt; = 0 sind, also würde ich das einfach machen.

    
Andy Morris 23.03.2009 22:17
quelle
0

Normalerweise mache ich in diesem Fall eine Option-Klasse (inspiriert von F #) und erweitere IEnumerable mit einer TryFind () -Methode, die die Option-Klasse zurückgibt.

%Vor%

}

Dann können Sie es als

verwenden %Vor%

Auf diese Weise wird die Sammlung unabhängig davon, ob das Element vorhanden ist oder nicht, nur einmal durchlaufen.

    
user61392 24.03.2009 00:57
quelle
-1

Eine Ausnahme sollte etwas "außergewöhnlich" sein. Also, wenn Sie Find aufrufen, und Sie erwarten, etwas zu finden, egal was, dann eine Ausnahme zu werfen, wenn Sie etwas nicht finden, ist gutes Verhalten.

Wenn es jedoch der normale Fluss ist, dass Sie manchmal etwas nicht finden, dann ist das Werfen einer Ausnahme schlecht.

    
GvS 23.03.2009 17:22
quelle

Tags und Links