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.
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.
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.
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.
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.
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).
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.
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 :.
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.
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%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%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.
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.
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.
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.
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.