Welche Ausnahmen sollten für ungültige oder unerwartete Parameter in .NET ausgelöst werden?


163

Welche Arten von Ausnahmen sollten für ungültige oder unerwartete Parameter in .NET ausgelöst werden? Wann würde ich eine anstelle einer anderen wählen?

Nachverfolgen:

Welche Ausnahme würden Sie verwenden, wenn Sie eine Funktion haben, die eine Ganzzahl erwartet, die einem Monat entspricht, und Sie '42' übergeben haben? Würde dies in die Kategorie "außerhalb des Bereichs" fallen, obwohl es sich nicht um eine Sammlung handelt?

Antworten:


249

Ich mag zu verwenden: ArgumentException, ArgumentNullException, und ArgumentOutOfRangeException.

Es gibt auch andere Optionen, die sich nicht so sehr auf das Argument selbst konzentrieren, sondern den Aufruf als Ganzes beurteilen:

  • InvalidOperationException- Das Argument ist möglicherweise in Ordnung, jedoch nicht im aktuellen Status des Objekts. Das Guthaben geht an STW (früher Yoooder). Stimmen Sie auch seine Antwort ab.
  • NotSupportedException- Die übergebenen Argumente sind gültig, werden in dieser Implementierung jedoch nicht unterstützt. Stellen Sie sich einen FTP-Client vor und Sie übergeben einen Befehl, den der Client nicht unterstützt.

Der Trick besteht darin, die Ausnahme auszulösen, die am besten ausdrückt, warum die Methode nicht so aufgerufen werden kann, wie sie ist. Im Idealfall sollte die Ausnahme detailliert beschrieben werden, was schief gelaufen ist, warum es falsch ist und wie es behoben werden kann.

Ich liebe es, wenn Fehlermeldungen auf Hilfe, Dokumentation oder andere Ressourcen verweisen. Zum Beispiel hat Microsoft mit seinen KB-Artikeln einen guten ersten Schritt getan, z. B. "Warum erhalte ich die Fehlermeldung" Vorgang abgebrochen ", wenn ich eine Webseite in Internet Explorer besuche?" . Wenn Sie auf den Fehler stoßen, verweisen sie auf den KB-Artikel in der Fehlermeldung. Was sie nicht gut machen, ist, dass sie dir nicht sagen, warum es speziell fehlgeschlagen ist.

Nochmals vielen Dank an STW (ex Yoooder) für die Kommentare.


Als Antwort auf Ihr Follow-up würde ich eine werfen ArgumentOutOfRangeException. Sehen Sie sich an, was MSDN zu dieser Ausnahme sagt:

ArgumentOutOfRangeExceptionwird ausgelöst, wenn eine Methode aufgerufen wird und mindestens eines der an die Methode übergebenen Argumente keine Nullreferenz ( Nothingin Visual Basic) ist und keinen gültigen Wert enthält.

In diesem Fall übergeben Sie also einen Wert, der jedoch kein gültiger Wert ist, da Ihr Bereich zwischen 1 und 12 liegt. Die Art und Weise, wie Sie es dokumentieren, macht jedoch deutlich, was Ihre API auslöst. Denn obwohl ich sagen könnte ArgumentOutOfRangeException, könnte ein anderer Entwickler sagen ArgumentException. Machen Sie es sich einfach und dokumentieren Sie das Verhalten.


Psst! Ich bin damit einverstanden, dass Sie seine spezifische Frage genau richtig beantwortet haben, aber siehe meine Antwort unten, um die defensive Codierung und Validierung von Parametern abzurunden ;-D
STW

+1, aber zu dokumentieren, welche Ausnahme ausgelöst wird und warum, ist wichtiger als die Auswahl der 'richtigen'.
pipTheGeek

@pipTheGeek - Ich denke, es ist wirklich ein strittiger Punkt. Während die Dokumentation auf jeden Fall wichtig ist, erwartet sie auch, dass der konsumierende Entwickler proaktiv oder defensiv ist und die Dokumentation tatsächlich im Detail liest. Ich würde mich für einen freundlichen / beschreibenden Fehler gegenüber einer guten Dokumentation entscheiden, da der Endbenutzer die Möglichkeit hat, einen von ihnen und nicht den anderen zu sehen. Es gibt eine bessere Chance, dass ein Endbenutzer einem schlechten Programmierer einen Beschreibungsfehler mitteilt, als dass ein schlechter Programmierer die vollständigen Dokumente liest
STW

4
Wenn Sie ArgumentException abfangen, wird auch ArgumentOutOfRange abgefangen.
Steven Evers

Wie wäre es mit FormatException: Die Ausnahme, die ausgelöst wird, wenn das Format eines Arguments ungültig ist oder wenn eine zusammengesetzte Formatzeichenfolge nicht gut gebildet ist.
Anthony

44

Ich habe für Joshs Antwort gestimmt , möchte aber noch eine zur Liste hinzufügen:

System.InvalidOperationException sollte ausgelöst werden, wenn das Argument gültig ist, das Objekt sich jedoch in einem Zustand befindet, in dem das Argument nicht verwendet werden sollte.

Update aus MSDN entnommen:

InvalidOperationException wird in Fällen verwendet, in denen der Fehler beim Aufrufen einer Methode auf andere Gründe als ungültige Argumente zurückzuführen ist.

Angenommen, Ihr Objekt verfügt über eine PerformAction-Methode (enmSomeAction action). Gültige enmSomeActions sind Open und Close. Wenn Sie PerformAction (enmSomeAction.Open) zweimal hintereinander aufrufen, sollte der zweite Aufruf die InvalidOperationException auslösen (da das Arugment gültig war, jedoch nicht für den aktuellen Status des Steuerelements).

Da Sie bereits das Richtige tun, indem Sie defensiv programmieren, muss ich noch eine weitere Ausnahme erwähnen: ObjectDisposedException. Wenn Ihr Objekt IDisposable implementiert, sollte immer eine Klassenvariable vorhanden sein, die den bereitgestellten Status verfolgt. Wenn Ihr Objekt entsorgt wurde und eine Methode darauf aufgerufen wird, sollten Sie die ObjectDisposedException auslösen:

public void SomeMethod()
{
    If (m_Disposed) {
          throw new ObjectDisposedException("Object has been disposed")
     }
    // ... Normal execution code
}

Update: Um Ihre Nachverfolgung zu beantworten: Es handelt sich um eine mehrdeutige Situation, die durch einen generischen Datentyp (nicht im Sinne von .NET Generics), der zur Darstellung eines bestimmten Datensatzes verwendet wird, etwas komplizierter wird. Eine Aufzählung oder ein anderes stark typisiertes Objekt wäre idealer - aber wir haben diese Kontrolle nicht immer.

Ich würde mich persönlich der ArgumentOutOfRangeException zuwenden und eine Nachricht bereitstellen, die angibt, dass die gültigen Werte 1-12 sind. Meine Argumentation ist, dass Sie, wenn Sie über Monate sprechen und davon ausgehen, dass alle ganzzahligen Darstellungen von Monaten gültig sind, einen Wert im Bereich von 1-12 erwarten. Wenn nur bestimmte Monate (wie Monate mit 31 Tagen) gültig wären, würden Sie sich nicht mit einem Bereich an sich befassen, und ich würde eine generische ArgumentException auslösen, die die gültigen Werte angibt, und ich würde sie auch in den Kommentaren der Methode dokumentieren.


1
Guter Punkt. Dies könnte den Unterschied zwischen ungültiger und nicht erwarteter Eingabe erklären. +1
Daniel Brückner

Psst, ich stimme dir zu, du würdest deinen Donner einfach nicht stehlen. Aber da Sie darauf hingewiesen haben, habe ich meine Antwort aktualisiert
JoshBerke

38

Abhängig vom tatsächlichen Wert und welcher Ausnahme am besten passt:

Wenn dies nicht genau genug ist, leiten Sie einfach Ihre eigene Ausnahmeklasse von ab ArgumentException.

Yoooders Antwort erleuchtete mich. Eine Eingabe ist ungültig, wenn sie zu keinem Zeitpunkt gültig ist, während eine Eingabe unerwartet ist, wenn sie für den aktuellen Status des Systems nicht gültig ist. Im späteren Fall InvalidOperationExceptionist also eine vernünftige Wahl.


6
Entnommen aus der MSDN-Seite zu InvalidOperationException: "InvalidOperationException wird in Fällen verwendet, in denen der Fehler beim Aufrufen einer Methode auf andere Gründe als ungültige Argumente zurückzuführen ist."
STW


3

ArgumentException :

ArgumentException wird ausgelöst, wenn eine Methode aufgerufen wird und mindestens eines der übergebenen Argumente die Parameterspezifikation der aufgerufenen Methode nicht erfüllt. Alle Instanzen von ArgumentException sollten eine aussagekräftige Fehlermeldung enthalten, die das ungültige Argument sowie den erwarteten Wertebereich für das Argument beschreibt.

Es gibt auch einige Unterklassen für bestimmte Arten von Invalidität. Der Link enthält Zusammenfassungen der Untertypen und wann sie gelten sollten.


1

Kurze Antwort:
Weder noch

Längere Antwort: Die
Verwendung von Argument * Exception (außer in einer Bibliothek, in der ein Produkt aktiviert ist, z. B. eine Komponentenbibliothek) ist ein Geruch. Ausnahmen sind die Behandlung von Ausnahmesituationen, keine Fehler und keine Mängel des Benutzers (dh des API-Verbrauchers).

Längste Antwort: Das
Auslösen von Ausnahmen für ungültige Argumente ist unhöflich, es sei denn, Sie schreiben eine Bibliothek.
Ich bevorzuge Behauptungen aus zwei (oder mehr) Gründen:

  • Behauptungen müssen nicht getestet werden, während Assertions dies tun, und der Test gegen ArgumentNullException sieht lächerlich aus (probieren Sie es aus).
  • Behauptungen kommunizieren den Verwendungszweck des Geräts besser und sind der ausführbaren Dokumentation näher als eine Spezifikation des Klassenverhaltens.
  • Sie können das Verhalten von Assertionsverletzungen ändern. Zum Beispiel ist bei der Debug-Kompilierung ein Meldungsfeld in Ordnung, sodass Ihre Qualitätssicherung Sie sofort damit trifft (Sie erhalten auch Ihre IDE in der Zeile, in der es passiert), während Sie im Komponententest einen Assertionsfehler als Testfehler angeben können .

So sieht die Behandlung von Null-Ausnahmen aus (offensichtlich sarkastisch):

try {
    library.Method(null);
}
catch (ArgumentNullException e) {
    // retry with real argument this time
    library.Method(realArgument);
}

Ausnahmen sind zu verwenden, wenn eine Situation erwartet wird, die jedoch außergewöhnlich ist (Dinge, die außerhalb der Kontrolle des Verbrauchers liegen, wie z. B. E / A-Fehler). Argument * Ausnahme ist ein Hinweis auf einen Fehler und soll (meiner Meinung nach) mit Tests behandelt und mit Debug.Assert unterstützt werden

Übrigens: In diesem speziellen Fall hätten Sie den Monatstyp anstelle von int verwenden können. C # ist nicht ausreichend, wenn es um die Typensicherheit geht (Aspect # rulez!), Aber manchmal können Sie diese Fehler alle zusammen verhindern (oder zur Kompilierungszeit abfangen).

Und ja, MicroSoft ist falsch.


6
IMHO sollten Ausnahmen auch ausgelöst werden, wenn die aufgerufene Methode nicht vernünftigerweise fortfahren kann. Dies schließt den Fall ein, dass der Aufrufer falsche Argumente übergeben hat. Was würden Sie stattdessen tun? Rückgabe -1?
John Saunders

1
Wenn ungültige Argumente dazu führen würden, dass eine innere Funktion fehlschlägt, welche Vor- und Nachteile hat es, Argumente auf Gültigkeit zu testen, anstatt eine InvalidArgumentException aus der inneren Funktion abzufangen und mit einer informativeren zu versehen? Der letztere Ansatz scheint die Leistung im allgemeinen Fall zu verbessern, aber ich habe nicht viel gesehen.
Supercat

Eine schnelle Google-Suche um diese Frage zeigt, dass das Auslösen der allgemeinen Ausnahme die schlechteste Praxis von allen ist. In Bezug auf die Behauptung des Arguments sehe ich dies bei kleinen persönlichen Projekten als sinnvoll an, jedoch nicht bei Unternehmensanwendungen, bei denen ungültige Argumente höchstwahrscheinlich auf eine schlechte Konfiguration oder ein schlechtes Verständnis der Anwendung zurückzuführen sind.
Max

0

Es gibt eine Standard-ArgumentException, die Sie verwenden oder eine Unterklasse erstellen und eine eigene erstellen können. Es gibt mehrere spezifische ArgumentException-Klassen:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

Was auch immer am besten funktioniert.


1
Ich bin in fast allen Fällen nicht einverstanden; Die bereitgestellten .NET-Argument * -Ausnahmeklassen werden sehr häufig verwendet und bieten Ihnen die Möglichkeit, genügend spezifische Informationen bereitzustellen, um den Verbraucher über das Problem zu informieren.
STW

Zur Verdeutlichung: Ich bin nicht einverstanden mit fast allen Fällen, in denen aus den Argument * Exception-Klassen abgeleitet wurde. Die Verwendung einer der .NET-Argumentausnahmen sowie einer beschreibenden und klaren Meldung bietet genügend Details für mehr oder weniger jede Situation, in der Argumente ungültig sind.
STW

stimmte zu, aber ich beschrieb nur die verfügbaren Optionen. Ich hätte definitiv klarer über die "bevorzugte" Methode sein sollen.
Scott M.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.