Das Lesen des Artikels von Eric Lippert über Ausnahmen war definitiv ein Augenschmaus darüber, wie ich mit Ausnahmen umgehen sollte, sowohl als Produzent als auch als Verbraucher. Ich kämpfe jedoch immer noch darum, eine Richtlinie zu definieren, wie man lästige Ausnahmen vermeiden kann.
Speziell:
- Angenommen, Sie haben eine Speichermethode, die fehlschlagen kann, weil a) jemand anderes den Datensatz vor Ihnen geändert hat oder b) der Wert, den Sie erstellen möchten, bereits vorhanden ist . Diese Bedingungen sind zu erwarten und nicht außergewöhnlich. Anstatt eine Ausnahme auszulösen, erstellen Sie eine Try-Version Ihrer Methode, TrySave, die einen Booleschen Wert zurückgibt, der angibt, ob das Speichern erfolgreich war. Aber wenn es fehlschlägt, wie wird der Verbraucher wissen, was das Problem war? Oder ist es am besten, eine Aufzählung mit dem Ergebnis "Ok / RecordAlreadyModified / ValueAlreadyExists" zurückzugeben? Mit integer.TryParse existiert dieses Problem nicht, da es nur einen Grund gibt, warum die Methode fehlschlagen kann.
- Ist das vorige Beispiel wirklich eine ärgerliche Situation? Oder wäre es in diesem Fall die bevorzugte Methode, eine Ausnahme auszulösen? Ich weiß, dass dies in den meisten Bibliotheken und Frameworks, einschließlich des Entity-Frameworks, der Fall ist.
- Wie können Sie entscheiden, wann Sie eine Try-Version Ihrer Methode erstellen möchten, anstatt im Voraus zu testen, ob die Methode funktioniert oder nicht? Ich folge derzeit diesen Richtlinien:
- Wenn die Möglichkeit einer Racebedingung besteht, erstellen Sie eine Testversion. Dies verhindert, dass der Verbraucher eine exogene Ausnahme abfangen muss. Zum Beispiel in der zuvor beschriebenen Save-Methode.
- Wenn die Methode zum Testen der Bedingung so gut wie alles tun würde, was die ursprüngliche Methode tut, erstellen Sie eine Try-Version. Zum Beispiel integer.TryParse ().
- Erstellen Sie in jedem anderen Fall eine Methode zum Testen der Bedingung.
exceptions
Mike
quelle
quelle
Antworten:
Gute Frage.
Die erste Frage, die mir in den Sinn kommt, lautet: Wenn die Daten bereits vorhanden sind, inwiefern ist das Speichern fehlgeschlagen ? Es hört sich sicher so an, als wäre es mir gelungen. Nehmen wir jedoch an, dass Sie tatsächlich viele verschiedene Gründe haben, warum eine Operation fehlschlagen kann.
Die zweite Frage, die mir in den Sinn kommt, lautet: Sind die Informationen, die Sie an den Benutzer zurückgeben möchten, umsetzbar ? Das heißt, werden sie auf der Grundlage dieser Informationen eine Entscheidung treffen?
Wenn die "Check Engine" -Lampe aufleuchtet, öffne ich die Motorhaube, stelle sicher, dass sich in meinem Auto ein Motor befindet, der nicht in Flammen steht, und bringe ihn in die Garage. Natürlich haben sie in der Garage alle Arten von speziellen Diagnosegeräten, die ihnen sagen, warum das Licht des Prüfmotors an ist, aber aus meiner Sicht ist das Warnsystem gut konstruiert. Es ist mir egal, ob das Problem darin besteht, dass der Sauerstoffsensor einen abnormalen Sauerstoffpegel im Brennraum aufzeichnet oder der Leerlaufdrehzahldetektor nicht angeschlossen ist oder was auch immer. Ich werde das Gleiche tun, nämlich jemand anderem das herausfinden lassen .
Interessiert es den Anrufer, warum das Speichern fehlgeschlagen ist? Werden sie etwas dagegen unternehmen, außer entweder aufzugeben oder es erneut zu versuchen?
Nehmen wir aus Gründen des Arguments an, dass der Aufrufer tatsächlich verschiedene Aktionen ausführen wird, je nachdem, warum die Operation fehlgeschlagen ist.
Die dritte Frage lautet: Ist der Fehlermodus außergewöhnlich ? Ich glaube , Sie könnten etwas verwirrend sein kann mit unexceptional . Ich würde mir vorstellen, dass zwei Benutzer versuchen, denselben Datensatz gleichzeitig zu ändern, und zwar in einer Ausnahmesituation, die jedoch möglich ist, und nicht in einer üblichen Situation.
Nehmen wir aus Gründen der Argumentation an, dass es nicht außergewöhnlich ist.
Die vierte Frage lautet: Gibt es eine Möglichkeit, die schlechte Situation rechtzeitig und zuverlässig zu erkennen?
Wenn die schlechte Situation in meinem "exogenen" Eimer ist, dann nein. Es gibt keine Möglichkeit, zuverlässig zu sagen, dass ein anderer Benutzer diesen Datensatz geändert hat. weil sie es möglicherweise ändern, nachdem Sie die Frage gestellt haben . Die Antwort ist abgestanden , sobald sie erstellt wurde.
Die fünfte Frage, die sich stellt, lautet: Gibt es eine Möglichkeit, die API so zu gestalten, dass die schlechte Situation verhindert werden kann?
Beispielsweise können Sie den Vorgang "Speichern" in zwei Schritten ausführen. Schritt eins: Erwerben Sie eine Sperre für den Datensatz, der geändert wird. Diese Operation ist entweder erfolgreich oder schlägt fehl und kann daher einen Booleschen Wert zurückgeben. Der Anrufer kann dann festlegen, wie mit Fehlern umgegangen werden soll: Warten Sie eine Weile und versuchen Sie es erneut. Schritt 2: Sobald die Sperre aktiviert ist, speichern Sie sie und geben Sie die Sperre frei. Nun ist das Speichern immer erfolgreich und Sie müssen sich keine Gedanken mehr über die Fehlerbehandlung machen. Wenn das Speichern fehlschlägt, ist das wirklich außergewöhnlich.
quelle
Wenn in Ihrem Beispiel die ValueAlreadyExists-Situation einfach überprüft werden kann, sollte sie überprüft und eine Ausnahme ausgelöst werden, bevor das Speichern versucht wird. Ich denke nicht, dass in dieser Situation ein Versuch erforderlich sein sollte. Es ist schwieriger, die Rennbedingungen im Voraus zu überprüfen. In diesem Fall ist es wahrscheinlich eine sehr gute Idee, das Speichern in einen Versuch zu packen.
Im Allgemeinen, wenn es eine Bedingung gibt, die meiner Meinung nach sehr wahrscheinlich ist (wie NoDataReturned, DivideByZero usw.) ODER die sehr einfach zu überprüfen ist (wie eine leere Auflistung oder ein NULL-Wert), versuche ich zu überprüfen bevor ich zu dem Punkt komme, an dem ich eine Ausnahme erwischen müsste. Ich gebe zu, es ist nicht immer einfach, diese Bedingungen im Voraus zu kennen, manchmal erscheinen sie nur, wenn der Code strengen Tests unterzogen wird.
quelle
Das
save()
Methode muss eine Ausnahme auslösen.Die oberste Ebene sollte den Benutzer erfassen und informieren , ohne das Programm zu beenden, es sei denn, es handelt sich um ein Unix-ähnliches Befehlszeilenprogramm. In diesem Fall ist es in Ordnung, das Programm zu beenden.
Rückgabewerte sind keine gute Möglichkeit, Ausnahmen zu verwalten.
quelle