Sollte ich meine eigenen Ausnahmen machen oder ähnliche Ausnahmen für leicht nicht standardmäßige Zwecke kooptieren?

8

Dies ist eine allgemeine Entwurfsfrage, aber ich konzentriere mich auf C # und .NET, da dies die Sprachen sind, mit denen ich gerade arbeite.

Sollte ich meine eigenen, neuen Ausnahmeklassen erstellen oder vorhandene Framework-Ausnahmen für etwas andere Zwecke kooptieren?

Ich benötige beispielsweise eine Ausnahme, die angibt, dass ein Mitglied (z. B. ein Typ) erwartet wurde, aber beim Lesen einer Assembly mit nicht gefunden wurde Mono.Cecil. Die Basisklassenbibliothek definiert die Ausnahme MissingMemberException, die genau das zu kommunizieren scheint, was ich will, aber die Beschreibung sagt:

Die Ausnahme, die ausgelöst wird, wenn versucht wird, dynamisch auf ein nicht vorhandenes Klassenmitglied zuzugreifen.

Das passt nicht genau, weil ich nicht dynamisch auf das Mitglied zugreife, sondern passiv mit der Assembly arbeite.

GregRos
quelle
Was Sie wollen, ist die Wiederverwendung des Namens, aber nicht des Typs. MissingMemberException hat eine bestimmte Bedeutung. Definieren Sie einfach einen neuen Typ, der für Ihr Framework spezifisch ist und genau die richtige Bedeutung hat.
usr

Antworten:

7

Allgemein

Das Unterklassifizieren von Ausnahmen ist eine gute Idee, um die Ausnahmen in die Familien oder Gruppierungen zu gruppieren, zu denen sie gehören. Außerdem kann Client-Code Ausnahmen behandeln, die auf bestimmten Fehlern oder allgemeiner basieren. Normalerweise teile ich den Baum irgendwo in der Nähe der Wurzel zwischen logischen und Laufzeitfehlern auf (z. B. in C ++ das std::runtime_errorvs. std::logic_error).

Es ist auch eine gute Idee, zwischen den Systemausnahmen und den Ausnahmen Ihrer Anwendung unterscheiden zu können. Es wäre seltsam, wenn der Client-Code nach einem Systemfehler Ausschau hält, aber den logischen Fehler Ihres (über eine Basisklasse) Codes abfängt.

In diesem Fall und C #

C # hat eine reichhaltige und große Ausnahmehierarchie - ich würde nicht versuchen, eine Basisklasse zu tief darin aufzunehmen. Wenn Sie Ihrem Beispiel folgen, scheint es, als würden Sie einen logischen Fehler (oder zumindest einen Anwendungsfehler) auslösen, sodass die Basisklasse oder auch nur diese Ausnahme auf einen Systemfehler hinweisen würde. Die genaue Ausnahme kann diskutiert werden, aber ich denke in diesem Fall ist es keine gute Wahl.

Hierarchie

Ich würde eine Hierarchie erstellen, die es Ihnen ermöglicht, zwischen System- und Anwendungsfehlern und dann weiter zwischen Laufzeit- und logischen Fehlern zu unterscheiden. Haben Sie keine Angst davor, innere Ausnahmen zu verwenden - fangen Sie den Systemfehler ab und wickeln Sie ihn in etwas Bedeutenderes ein, wenn man bedenkt, in welchen Kontext er geworfen wird. Gehen Sie nicht über Bord, sondern konzentrieren Sie sich auf die wichtigsten Ausnahmen. Mit Ausnahmen können eindeutige Textbeschreibungen verknüpft sein. Verwenden Sie diese, um dem Benutzer noch mehr Details oder Hinweise zu geben, wie das Problem behoben werden kann.

Niall
quelle
2
Dies ist eine brillante Strategie, wenn Ausnahmen untergeordnet werden. Ich bin mir nicht sicher, ob Sie in jedem Fall Ausnahmen zu Unterklassen sagen oder einfach sagen, dass Sie dies tun sollten, wenn Sie Unterklassen bilden. In Bezug darauf, ob Sie eine Unterklasse haben sollten, stimme ich der Antwort @DanielS zu.
Chuck Conway
3

Die Antwort lautet natürlich "es kommt darauf an", aber angesichts Ihres Anwendungsfalls würde ich nein sagen. Hier ist der Grund:

C # ist eine wunderbare - Kratzer, die beste - objektorientierte Sprache. Es ist in der Tat so gut, dass ich manchmal versucht werde, eine fanatisch puristische Vision von dem umzusetzen, was als einfaches Projekt begann. Das passiert mir beim Schreiben von JavaScript, Objective-C oder PHP-Code nicht so sehr.

Wenn ich in diesen "Modus", diese Art von Lähmung, gerate, kann eines der Symptome eine Besessenheit von Ausnahmebehandlung und Generika und Metaprogrammierung sein. Manchmal alle drei zusammen. Es ist weitaus wahrscheinlicher, dass ich ein Problem habe, wenn ich an etwas Neuem, relativ Unbekanntem oder mit schlechten Spezifikationen und Zielen arbeite. Anstatt mich in den Implementierungsdetails festzumachen , denke ich, warum sollte ich versuchen, etwas zu schaffen, das die meisten Szenarien berücksichtigt, die ich vernünftigerweise vorwegnehme? Wenn ich mich dann um die Details kümmere, wird der Grundstein gelegt und ich werde ein paar stumpfe kleine Methoden schreiben und so weiter. Vielleicht wird jemand anderes am UI-Code arbeiten, ich werde ihnen nur diese netten Dienste und diese Verträge anbieten ...

Das ist mir leider noch nicht passiert. Was dazu neigt, ist, dass ich diesen Weg beschreite und an "Infrastruktur" arbeite - Dinge wie:

  • Schreiben meiner eigenen IoC-Container-Schnittstellen oder anderweitiges Herumschrauben Castle.Windsor
  • Festlegen, wie mit der Typzuordnung in der Anwendung umgegangen werden soll
  • Schreiben der Kontrollflusslogik für abstrakte Basisklassen, die Aufrufen von abstrakten Mitgliedern mit Ausnahmebehandlung ähneln, Interceptors und Umschließen von Aufrufen
  • Erstellen von abstrakten Basisklassen für IRepository, IUnitOfWork, IDomainService, ICrossCuttingValidation, usw.
  • Der Versuch, ein hierarchisches Objektmodell für etwas zu beschreiben, dessen Natur einen solchen Meta-Ansatz leugnet ( wieIFrameworkException )

Wenn Sie sich nicht sicher sind, ob die Beschreibung einer Ausnahme genau genug für Ihren Geschmack ist, verweigern Sie wirklich die Implementierung einer anderen wichtigen Funktion. Die Art der Ausnahme ist ebenfalls aussagekräftig - es handelt sich nicht um eine ereignisgesteuerte Ausnahme, sie tritt offensichtlich weit entfernt von einer Eingabemethode (wie einer Benutzeroberfläche oder einer Pipe oder etwas anderem) auf und ist wahrscheinlich deterministischer Natur - wie in, können Sie nicht Vorhersagen, was ein Benutzer eingeben wird, aber Sie können vorhersagen, welche Baugruppe Sie laden möchten oder was nicht.

Übrigens, wenn Sie die Beschreibung so sehr stört, können Sie dann keine Erweiterungsmethode schreiben, um sie im entsprechenden Bereich zu überschreiben?

Das ist wirklich nur meine Art, meine eigene Überabstraktions- und Analyse-Lähmung auf Sie zu projizieren, aber ich denke, es könnte angemessen sein. Ich würde sagen, um auf der sicheren Seite zu bleiben, nur eine Unterklasse und Exceptionwann:

  • Sie haben tatsächlich eine Laufzeitausnahme festgestellt, die Sie im Allgemeinen behandeln möchten
  • Die Laufzeitausnahme kann zur Entwurfszeit nicht vernünftigerweise ausgeschlossen werden
  • Die Ausnahme wird durch die große Anzahl vorhandener ExceptionUnterklassen noch nicht gut abgedeckt
  • Sie haben tatsächlich eine Vorstellung davon, wie Sie mit der Ausnahme umgehen möchten ODER wenn nicht, nur weil sie so kompliziert ist, dass Sie darüber nachdenken und darauf zurückkommen müssen
  • Ihre Idee geht über die Änderung ihrer Metadaten, ihres Umfangs, ihrer inneren Ausnahme usw. hinaus und befasst sich mehr mit dem zugrunde liegenden Objekt oder Ereignis, das die Ausnahme erzeugt, und / oder den Mitteln, um tatsächlich damit umzugehen
  • Sie haben bereits geschrieben oder sind nicht für den Großteil der eingabebezogenen Seite von Dingen wie der Benutzeroberfläche verantwortlich
tacos_tacos_tacos
quelle
Ich schätze Ihren pragmatischen Ansatz. Wie bei allem kommt es darauf an.
Chuck Conway
1

Aus meiner Sicht sind benutzerdefinierte Ausnahmen nur in zwei Fällen sinnvoll:

1) Sie entwickeln eine API

2) Sie verwenden es in Ihrem Code, um einen Fehler zu beheben.

In jedem anderen Szenario sollten integrierte Ausnahmen ausreichend sein.

In Ihrem Fall könnten Sie InvalidArgumentException verwenden

DanielS
quelle
Sie sollten Ihre Antwort ein wenig erweitern und jedem Aufzählungspunkt ein "Warum" geben. Sie sind hier genau richtig. Das Erstellen benutzerdefinierter Ausnahmen ist ein weiteres System / eine weitere Hierarchie, die beibehalten werden muss.
Chuck Conway