10 Stimmen

Auslösen einer Ausnahme des richtigen Typs

In meinem Code habe ich oft Situationen wie diese:

public void MyMethod(string data)
{
    AnotherClass objectOfAnotherClass = GetObject(data);
    if (objectOfAnotherClass == null)
        throw new WhatExceptionType1("objectOfAnotherClass is null.");

    if (objectOfAnotherClass.SomeProperty < 0)
        throw new WhatExceptionType2("SomeProperty must not be negative.");
}

Stellen Sie sich das vor GetObject einige externe Bibliotheken verwendet, die nicht unter meiner Kontrolle stehen, und dass diese Bibliothek null wenn kein Objekt für data existiert und eine negative SomeProperty als einen gültigen Zustand und löst daher keine Ausnahme aus. Stellen Sie sich weiter vor, dass MyMethod kann nicht funktionieren ohne objectOfAnotherClass und macht keinen Sinn bei einer negativen SomeProperty .

Welche Ausnahmen sind geeignet für WhatExceptionType1/2 in dieser Situation zu werfen?

Im Grunde hatte ich vier Optionen im Kopf:

  • 1) InvalidOperationException denn MyMethod ist unter den oben beschriebenen Bedingungen nicht sinnvoll. Andererseits sagen die Richtlinien (und auch Intellisense in VS), dass eine InvalidOperationException ausgelöst werden sollte, wenn das Objekt, zu dem die Methode gehört, in einem ungültigen Zustand ist. Nun ist das Objekt selbst nicht in einem ungültigen Zustand. Stattdessen ist der Eingabeparameter data und einige andere Operationen, die auf diesem Parameter basieren, führen zu einer Situation, in der MyMethod nicht mehr funktionieren.

  • 2) ArgumentException denn es gibt Werte für data mit denen die Methode arbeiten kann, und andere Werte, mit denen die Methode nicht arbeiten kann. Aber ich kann dies nicht durch eine Inspektion überprüfen data allein, muss ich andere Operationen anrufen, bevor ich mich entscheide.

  • 3) Exception weil ich nicht weiß, welchen anderen Ausnahmetyp ich verwenden soll, und weil mir alle anderen vordefinierten Ausnahmen zu speziell und nicht passend für meine Situation erscheinen.

  • 4) MyCustomException (mein eigener Ausnahmetyp, abgeleitet von Exception ). Dies scheint immer eine Option zu sein, aber ich bin besorgt, dass ich viele spezielle Ausnahmeklassen für viele verschiedene Fehlerbedingungen definieren muss, wenn ich anfange, diesem Muster zu folgen.

Gibt es andere und bessere Möglichkeiten? Was sind die Argumente für oder gegen diese Optionen?

Vielen Dank für Ihr Feedback im Voraus!

3voto

Stephen Cleary Punkte 402664

Kategorisieren Sie zunächst den Fehler. Eric Lippert in seinem Blog hat die beste Kategorisierung, die ich kenne: fatal, dumm, ärgerlich und exogen. Ihre Ausnahme wäre nicht fatal, sondern eine der folgenden: dumm, ärgerlich oder exogen.

Der Fehler ist dummes Zeug wenn Sie das für eine korrekte Eingabe sagen können name wissen Sie, dass GetObject wird ein Objekt zurückgeben, das für Ihre Methode sinnvoll ist. Mit anderen Worten, die einzige Ursache für diese Ausnahmen sind Fehler in dem Code, der MyMethod . In diesem Fall spielt es keine Rolle, welchen Ausnahmetyp Sie verwenden, da Sie ihn in der Produktion ohnehin nie sehen sollten. ArgumentException (wenn das Problem mit name ) oder InvalidOperationException (wenn das Problem mit dem Zustand des Objekts zusammenhängt, das MyMethod ) wären in dieser Situation eine gute Wahl, aber der spezifische Ausnahmetyp sollte nicht dokumentiert werden (sonst wird er Teil der API).

Der Fehler ist lästig wenn GetObject ist vorhersehbar in Bezug auf name (d.h. für eine bestimmte name erhalten Sie entweder immer ein gültiges Objekt oder nie ein gültiges Objekt), aber name basiert auf Benutzereingaben (oder Konfiguration). In diesem Fall wollen Sie überhaupt keine Ausnahme auslösen; Sie wollen eine TryMyMethod Methode, damit der aufrufende Code keine Ausnahme abfangen muss, um eine nicht außergewöhnliche Situation zu bewältigen.

Der Fehler ist exogenes wenn GetObject ist das Verhalten in Bezug auf die name : Ein äußerer Einfluss kann dazu führen, dass name gültig oder ungültig werden. In diesem Fall müssen Sie eine bestimmte Ausnahmeart wählen und dokumentieren sie als Teil der API. ArgumentException wäre keine gute Wahl für eine exogene Ausnahme; ebenso wenig wäre InvalidOperationException . Sie sollten eher etwas wählen wie FileNotFoundException das zugrundeliegende Problem beschreiben (was auch immer GetObject tut).

2voto

jpmc26 Punkte 25728

Ich weiß, dass diese Antwort viel, viel zu spät kommt, um für den OP von Nutzen zu sein, aber ich bin geneigt zu glauben, dass die Schwierigkeit hier eher ein Codegeruch ist. Wenn wir das Prinzip der einzigen Verantwortung auf der Ebene einer Methode und nicht nur einer Klasse betrachten, dann scheint diese Methode dagegen zu verstoßen. Sie tut 2 Dinge: Sie analysiert data und führt dann die weitere Verarbeitung durch. Das Richtige ist also, eine Verantwortung zu beseitigen, nämlich die des Parsens. Sie können dies tun, indem Sie Ihr Argument in eine Instanz von AnotherClass . Dann wird klar, was Sie tun sollten:

public void MyMethod(AnotherClass data)
{
    if (data == null)
        throw new ArgumentNullException("data is null.");

    if (data.SomeProperty < 0)
        throw new ArgumentException("data.SomeProperty must not be negative.");

    ...
}

Es liegt dann in der Verantwortung des Aufrufers, die Parsing-Methode aufzurufen und mit dieser Situation umzugehen. Er kann sich dafür entscheiden, die Ausnahme abzufangen oder sie vor dem Aufruf der Methode zu überprüfen. Der Aufrufer muss ein wenig mehr Arbeit leisten, hat aber den Vorteil einer größeren Flexibilität. Dies bietet zum Beispiel auch den Vorteil, dass alternative Konstruktionen von AnotherClass Der Aufrufer kann sie im Code aufbauen, anstatt sie zu parsen, oder eine andere Parsing-Methode aufrufen.

1voto

Chris Gessler Punkte 21932

Es hängt wirklich davon ab, wie Sie planen, Ausnahmen in Ihrer Anwendung zu behandeln. Benutzerdefinierte Ausnahmen sind schön in einem try/catch Situationen, aber try/catches sind auch teuer. Wenn Sie nicht vorhaben, Ihre benutzerdefinierte Ausnahme abzufangen und zu behandeln, dann: throw new Exception("Index außerhalb des Bereichs: SomeProperty darf nicht negativ sein."); ist genauso nützlich wie eine benutzerdefinierte Ausnahme.

public class InvalidStateException : ApplicationException
{
   ...
}

In Ihrem Code:

// test for null
if(objectOfAnotherClass == null) throw new NullReferenceException("Object cannot be null");

// test for valid state
if(objectOfAnotherClass.SomeProperty < 0) throw new InvalidStateException("Object is in an invalid state");

1voto

Wim.van.Gool Punkte 1230

Ich stimme dem Beitrag von Stephen Cleary zu, dass man versuchen sollte, seine Ausnahme zu kategorisieren und dann den Ausnahmetyp zu finden, der entsprechend geworfen wird. Ich finde die Kategorisierung von Eric Lippert sehr interessant und in vielerlei Hinsicht hat er recht, mais Ich habe über eine andere Art der Kategorisierung nachgedacht, die der Kategorisierung von Lippert ähnelt, mit dem Unterschied, dass ich mich mehr auf Code-Verträge . Was ich vorschlage, ist eine Kategorisierung Exceptions in gescheiterten Vorbedingungen , Nachkonditionen und einfach Fehler .

Die Sache ist die: Jede Methode hat einen formalen Vertrag in dem Sinne, dass sie, wenn Sie alle Vorbedingungen erfüllen, verspricht, einige Nachbedingungen für Sie zu erfüllen. Im Allgemeinen können alle Vorbedingungen in drei Arten unterteilt werden:

  • Sicherheitsrelevant (ist der Anrufer berechtigt, den Anruf zu tätigen?)
  • Kontextbezogen (befindet sich das Objekt im richtigen Zustand, um den Anruf zu tätigen?)
  • Eingabebezogen (hat der Aufrufer gültige Argumente angegeben?)

Im Prinzip sollte jede Methode diese Bedingungen der Reihe nach prüfen und eine der folgenden Ausnahmen auslösen (den genauen Typ oder die Ableitung):

  • SecurityException
  • InvalidOperationException
  • ArgumentException

Diese Ausnahmen teilen dem Anrufer mit, dass es "seine Schuld" war, dass etwas schief gelaufen ist und der Anruf nicht korrekt abgeschlossen werden konnte. Wenn jedoch alle Vorbedingungen gemäß der formalen Spezifikation der Methode erfüllt sind und die Methode feststellt, dass sie die angegebenen Nachbedingungen nicht erfüllen kann, sollte sie eine Exception von einer Art, die klar kommuniziert was schiefgegangen. In der Regel sollten Sie für diese Situationen einen eigenen Ausnahmetyp definieren oder einen vorhandenen Ausnahmetyp wiederverwenden, sofern es sich nicht um einen der Typen handelt, die für Vorbedingungsfehler reserviert sind.

Und schließlich, Fehler sind Exceptions die von der CLR ausgelöst werden, die praktisch überall und jederzeit auftreten können und praktisch nicht vorhersehbar sind. Sie werden nie explizit Teil Ihrer Methoden sein und sollten daher nie von Benutzercode ausgelöst (und auch nicht speziell behandelt) werden. In diesem Sinne sind sie fast eins zu eins mit den fatalen Ausnahmen von Lippert vergleichbar.

Was sollten Sie also meiner Meinung nach in dem von Slauma geschilderten Fall tun?

Nun, wie Sie sich vorstellen können, hängt dies ganz von den Verträgen der MyMethod(data) , GetObject(data) y objectOfAnotherClass.SomeProperty . Was bedeutet die API von GetObject(data) sagen, in welchen Situationen er zurückkehren wird null (oder seine eigenen Ausnahmen auslösen)? Was ist die genaue Bedeutung und Spezifikation von SomeProperty ?

Angenommen, dass GetObject(data) ist eine Methode, die ein aus einer Datenbank abgerufenes Objekt zurückgibt und die data ist der Bezeichner des Objekts. Wenn der Vertrag von GetObject(data) gibt an, dass er Folgendes zurückgeben wird null wenn kein Objekt mit Kennung data existiert, dann sollte Ihr Entwurf dies in seinem eigenen Vertrag berücksichtigen. Sie könnten den Aufrufer dazu zwingen, wenn das sinnvoll ist, immer einen Wert für data tal que GetObject(data) kehrt nicht zurück null und wenn dies der Fall ist, können Sie eine ArgumentException (oder Ableitung), um den Fehler auf der Seite des Anrufers anzuzeigen.

Andererseits, wenn GetObject(data) festgelegt hat, dass es nur dann null zurückgibt, wenn der Aufrufer nicht genügend Rechte hat, um das Objekt abzurufen, können Sie eine SecurityException (oder Ableitung), um dem Aufrufer von MyMethod(data) .

Schließlich, wenn GetObject(data) verspricht, dass er "niemals" zurückkehren wird null und es trotzdem tut, können Sie Ihren Code mit einer NullReferenceException (weil wir zu Recht davon ausgehen, dass es nie sein wird null ) oder behandeln Sie die Situation speziell, wenn Sie es mit sensiblem Code zu tun haben, indem Sie Ihren eigenen Exception-Typ auslösen (da eine Nachbedingung aus Sicht des Aufrufers von MyMethod fehlgeschlagen ist).

Der zweite Fall ist etwas komplizierter, lässt sich aber auf die gleiche Weise angehen. Angenommen, dass objectOfAnotherClass eine Zeile oder Entität präsentiert, die aus einer Datenbank über einen Bezeichner abgerufen wurde data y MyMethod(data) eindeutig festgelegt, dass data sollte einen Bezeichner eines Objekts angeben, bei dem objectOfAnotherClass.SomeProperty >= 0 dann würde in diesem Fall das Auslösen einer ArgumentException (oder eines Derivats) zu Ihrem Entwurf gehören.

Andererseits, wenn MyMethod(data) arbeitet in einem Kontext, in dem der Aufrufer davon ausgehen kann, dass ein gültiger Bezeichner niemals ein Objekt zurückgeben wird, das objectOfAnotherClass.SomeProperty < 0 (oder besser: nicht einmal weiß, dass es ein solches Objekt gibt), dann ist ein solches Auftreten auf der Seite des Aufrufers wirklich unerwartet und MyMethod(data) sollte den Fall entweder gar nicht explizit prüfen (weil man davon ausgeht, dass er nicht auftritt) oder, wenn man einen robusteren Code anstrebt, eine spezielle, benutzerdefinierte Exception auslösen, die das Problem anzeigt.

Zum Schluss: Ich denke, die Bestimmung der Art der Exception zu werfen, hängt ausschließlich von den formalen Verträgen ab, die jede Methode mit ihren Aufrufern und Aufgerufenen hat. Wenn der Aufrufer eine Vorbedingung nicht erfüllt, sollte eine Methode dem Aufrufer immer mit dem Auslösen eines SecurityException , InvalidOperationException ou ArgumentException . Wenn die Methode selbst ihre eigenen Nachbedingungen nicht erfüllt, kann sie entweder bei Ausnahmen, die von der CLR oder anderen Komponenten ausgelöst werden, abstürzen oder eine eigene, spezifischere Ausnahme auslösen, die das Problem angibt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X