it-swarm.com.de

Sollten Sie eine Ausnahme auslösen, wenn die Eingabewerte einer Methode außerhalb des Bereichs liegen?

Sollten Sie eine Ausnahme auslösen, wenn die Eingabewerte einer Methode außerhalb des Bereichs liegen? z.B

//no imaginary numbers
public int MySquareRoot(int x)
{
    if (x<0)
    {
    throw new ArgumentOutOfBoundsException("Must be a non-negative integer"); 
    }

    //our implementation here 

}

Jetzt sollte diese Methode niemals mit einer nicht negativen Zahl aufgerufen werden, aber hey, Programmierer machen Fehler. Ist es das Richtige, hier Ausnahmen zu werfen?

16
dwjohnston

Ja, ich glaube, Sie sollten eine Ausnahme auslösen, damit Ihre Programmierkollegen den Fehler beim Kompilieren bemerken, indem Sie Ihrer Methodendeklaration auch throws ArgumentOutOfBoundsException Hinzufügen.

Das heißt, es sei denn, Sie verwenden in Ihrem Projekt imaginäre Zahlen, wobei -1 ein vollkommen gültiges Argument für Ihre Quadratwurzelmethode ist. :) :)

20

Wenn Sie Ihre Methode oder ihre Signatur so codieren können, dass die Ausnahmebedingung nicht eintreten kann, ist dies wahrscheinlich der beste Ansatz.

Hat Ihre Sprache beispielsweise in dem von Ihnen angesprochenen Beispiel einen vorzeichenlosen ganzzahligen Datentyp mit einem geeigneten Bereich? Wenn ja, verwenden Sie einfach diesen anstelle eines entsprechenden vorzeichenbehafteten Integer-Typs und lösen Sie eine Ausnahme für einen übergebenen Wert aus, der kleiner als 0 ist. In diesem Fall kann ein negativer Wert an dieser Position in einem wohlgeformten Programm nicht verwendet werden. Der Compiler wird den Fehler lange vor der Laufzeit abfangen und zumindest eine Warnung ausgeben (für signierte/nicht signierte Typen verwenden Sie eine Nichtübereinstimmung), es sei denn, Sie geben sich alle Mühe, dieses Sicherheitsnetz explizit zu umgehen ;; und wenn Sie eine vorzeichenbehaftete Variable in eine Funktion zwingen, deren Methodensignatur nur eine vorzeichenlose Ganzzahl angibt, ohne sicherzustellen, dass der Wert innerhalb eines akzeptablen Bereichs für diese Funktion liegt, würde ich argumentieren, dass der Programmierer dies verdient, eine Ausnahme ausgelöst zu bekommen in ihrem Gesicht.

Wenn nur ein einigermaßen kleiner Satz von Werten als Eingabe gültig ist, können diese möglicherweise als Aufzählung mit einem bestimmten Satz von Werten ausgedrückt werden, anstatt einen einfachen ganzzahligen Wert zum Codieren jedes möglichen Werts zu verwenden. Selbst wenn die Aufzählung unter der Haube zu einem int zusammenfällt, schützt die Verwendung von Aufzählungen vor Programmiererfehlern.

Wenn Sie die Methodensignatur nicht so schreiben können, dass die Ausnahmebedingung in einem wohlgeformten Programm möglicherweise nicht auftreten kann, würde ich argumentieren, dass dies der Fall ist genau wofür ArgumentOutOfRangeException , IllegalArgumentException und ähnliche Ausnahmetypen bestimmt sind. Dies kann auch trivial in einen TDD-Workflow eingearbeitet werden. Stellen Sie nur sicher, dass der Fehler klar ist ; Insbesondere wenn Sie keinen einfachen Zugriff auf den Quellcode haben, kann es sehr frustrierend sein, wenn eine unspezifische ArgumentOutOfRangeException zurückgeworfen wird. Beachten Sie die Dokumentation für diese Ausnahmetypen:

ArgumentOutOfRangeException (.NET) : "Die Ausnahme, die ausgelöst wird, wenn der Wert eines Arguments außerhalb des zulässigen Wertebereichs liegt, der von der aufgerufenen Methode definiert wird." ( wie hier zu sehen )

IllegalArgumentException (Java) : "Wird ausgelöst, um anzuzeigen, dass einer Methode ein illegales oder unangemessenes Argument übergeben wurde." ( wie hier zu sehen )

Das Schreiben der Methode, um die Ausnahmebedingung zunächst unmöglich zu machen, hilft noch mehr als das Auslösen einer Ausnahme zur Laufzeit. Wir müssen jedoch anerkennen, dass dies in der Praxis manchmal einfach nicht möglich ist. Sie müssen jedoch zur Laufzeit noch Invarianten überprüfen, die nicht direkt mit dem Bereich eines einzelnen Parameterwerts zusammenhängen. Wenn zum Beispiel x + y> 20 ein Fehler ist, aber x und y jeweils alles <= 20 sein können, gibt es meines Erachtens keine einfache Möglichkeit, dies in der Methodensignatur auszudrücken.

15
a CVn

Ich verstehe Ihre Frage als "Sollten Sie Eingabedaten validieren?" im Gegensatz zu "Sollten Sie Ausnahmen anstelle von Fehlercodes verwenden?".

Wenn Sie Eingabeparameter von außen akzeptieren - sei es eine Methode mit Aufrufparametern, XML-Eingabedaten von einem Serviceabruf oder eine Webseite mit Benutzerfeldern - haben Sie grundsätzlich zwei Möglichkeiten:

  1. rigorus prüft, ob die Eingabedaten mit dem erwarteten Format übereinstimmen
  2. vertrauen Sie Ihrem Anrufer, dass er Sie nur mit korrekten Daten anruft

Das Hinzufügen von Schecks erhöht die Komplexität

  1. der ursprüngliche Entwickler muss an alle gültigen (oder "möglicherweise ungültigen") Werte denken und entsprechende Überprüfungen hinzufügen. Idealerweise erfolgt dies zu Beginn des Verfahrens, obwohl es manchmal später erfolgen muss (z. B. wenn "gültiger Eingabewert" "die ID eines tatsächlich in der Datenbank vorhandenen Objekts" ist).
  2. weitere Entwickler versuchen, die eigentliche Logik der Methode zu verstehen, da sie weitere zu untersuchende Zeilen hinzufügt.
  3. Laufzeit-Overhead , um alle Annahmen zu überprüfen

Andererseits bringt die Eingabevalidierung wertvolle Vorteile:

  1. Es ermöglicht Ihnen, Annahmen tiefer in der Codierung zu treffen, da diese bereits überprüft wurden
  2. weitere Entwickler müssen möglicherweise mehr lesen, aber wenn Sie Annahmen über Eingabedaten explizit machen , können Sie die Logik besser verstehen und Eckfälle berücksichtigen.
  3. Es stellt sicher, dass keine beschädigten Programmzustände (Abstürze, Datenbeschädigung, Sicherheitslücken) auftreten können.

Daher sollte die Auswahl des Hinzufügens von Überprüfungen getroffen werden im Kontext wo Ihre Methode verwendet wird.

  • Ist es nicht vertrauenswürdigen externen Daten wie Websites oder öffentlichen APIs ausgesetzt? Dann machen Sie auf jeden Fall eine gründliche Validierung.
  • ist es eine private Methode in einer oft als innere Schleife bezeichneten Methode, die durch äußere Codierung geschützt ist? Dann möchten Sie wahrscheinlich keine redundanten Überprüfungen. Möglicherweise möchten Sie Ihre Erwartungen dokumentieren (z. B. JavaDoc), obwohl dies in der Praxis selten der Fall ist.
  • einige Sprachen bieten bedingt kompilierte Zusicherungen, die in Debug-Builds zum Abfangen von Programmlogikfehlern aktiviert, für die Leistung bei produktiver Verwendung jedoch deaktiviert sind
  • für Bibliotheken müssen Sie entscheiden, wie sehr Sie Ihren Anrufern vertrauen. Gehen Sie besser auf Nummer sicher und fügen Sie Überprüfungen hinzu, insbesondere wenn Sie nicht möchten, dass Ihre Bibliotheksinvarianten kompromittiert werden. Für geschwindigkeitsrelevante Funktionen können Sie sich jedoch anders entscheiden (und dokumentieren).
  • in der Regel wird eine Mischung erstellt: eine gute Validierung auf extern ausgerichteten Methoden, eine gewisse Validierung auf Schnittstellenebene, sehr wenige (oft nur NullPointerChecks) im Innenleben eines Programms.

Wenn Ihre Frage jedoch "Sind Ausnahmen der richtige Weg, um ungültige Eingabedaten zu signalisieren?" lautete, lautet die Antwort: höchstwahrscheinlich ja.

Ausnahmen verursachen einen erheblichen Overhead für die Laufzeitleistung, erleichtern jedoch das Nachdenken über den Programmablauf drastisch. Dies reduziert fehlerhafte Programmierung (semantische Fehler), insbesondere da Sie gezwungen sind, mit ihnen umzugehen. Sie schlagen "sicher fehl", indem Sie das Programm beenden, wenn sie ignoriert werden. Sie sind ideal für "Situationen, die nicht passieren sollen". Außerdem können sie Metadaten wie eine Stapelverfolgung transportieren.

Fehlercodes hingegen sind leicht und schnell, zwingen den Methodenaufrufer jedoch, sie explizit zu überprüfen. Andernfalls kommt es häufig zu Programmfehlern, die von stiller Datenbeschädigung, Sicherheitslücken bis hin zu netten Feuerwerkskörpern reichen können, wenn Ihr Programm zufällig in einer Weltraumrakete ausgeführt wird.

Ein Beispiel, bei dem ich persönlich Fehlercodes anstelle von Ausnahmen bevorzugt hätte, ist die String.parseInt () -Methode in Java. Das Abrufen einer nichtstelligen Zeichenfolge von einer Eingabequelle (z. B. einem Benutzer) ist nicht völlig unerwartet, und ich muss mich auf jeden Fall damit befassen - manchmal so einfach wie die Verwendung eines Standardwerts. Als Aufrufer habe ich keine Möglichkeit, die Daten zu überprüfen, ob sie eine Ausnahme hervorrufen würden (ohne die Validatorlogik selbst zu implementieren). Daher ist die Generierung der Ausnahme als Teil des normalen Programmflusses und die Verwendung von Try-Catch-Blöcken die einzige Wahl .

Bitte beachten Sie, dass Fehlercodes sowohl In-Band- als auch Out-of-Band sein können:

  • In-Band: Ein spezieller (magischer) Wert wird als Marker für die Fehlerbedingung deklariert. Oft wird hier null oder -1 verwendet, z. in String-Suchmethoden
  • Out-of-Band: Es wird ein separater Wert zurückgegeben, der "kein Fehler" (normalerweise 0 oder null) oder den spezifischen Fehler angibt. Dies kann entweder als zusätzlicher Rückgabeparameter (für Sprachen, die mehrere Rückgabewerte unterstützen - z. B. Googles GO unterstützt dies insbesondere für Fehler) oder als Anforderung zum Aufrufen einer speziellen "getError ()" - Methode erfolgen.
9
Zefiro

Wenn ich eine Funktion aufrufe, erwarte ich, dass sie etwas bewirkt. Wenn es nicht das kann, was ich angefordert habe, sollte ich benachrichtigt werden - die Funktion sollte nichts tun oder etwas falsch machen.

Ich kann benachrichtigt werden, indem ein bestimmter Rückgabewert zurückgegeben wird (z. B. könnte eine Gleitkomma-sqrt-Funktion NaN für eine falsche Zahl zurückgeben), eine externe Fehlervariable festgelegt oder eine Ausnahme ausgelöst wird. (Der erste oder dritte sind bevorzugt.)

In Ihrem Fall kann Ihre Funktion nicht das tun, was von ihr verlangt wird, wenn x kleiner als Null ist. Es ist durchaus akzeptabel und informativ, eine Ausnahme zu machen, die besagt, dass das Argument außerhalb des Bereichs liegt.

5
paul

Sie sollten etwas tun, wie diese Antwort sagt , Ausnahmen sind hilfreich, es ist gut, schnell zu versagen usw. usw. Tatsächlich würde ich hinzufügen, dass sie beim Lesen helfen, ich würde sagen, dass sie die beabsichtigte Eingabe dokumentieren besser als der oberste Kommentar in Ihrem Beispiel.

Es gibt jedoch Alternativen. Wenn ich Ihre Sprachen richtig vermutet habe: Debug.Assert(x > 0) und Contract.Requires( x > 0 );

Der Konsens scheint jedoch zu sein, dass die Verwendung von Ausnahmen in einem Release-Build der beste Ansatz ist.

Asserts scheinen für das Debuggen gedacht zu sein, und es ist sinnvoller, mit Ausnahmen zu versenden, und dass Verträge noch nicht ganz vorhanden sind und auch nicht für öffentliche Methoden geeignet sind. Es ist nicht trivial und eignet sich nicht für eine schnelle Zusammenfassung. Ich schlage vor, diese Seite zu lesen.

Insgesamt würde ich eher Ausnahmen bevorzugen, aber Behauptungen könnten auch für interne Methoden eine gute Wahl sein.

4
Nathan Cooper

Ich möchte noch einen weiteren Grund hinzufügen, warum das Auslösen einer Ausnahme hier die beste Wahl ist: Unit-Test und TDD.

Jedes ausgereifte Testframework sollte in der Lage sein, zu behaupten, dass eine bestimmte Methode bei einer bestimmten Eingabe ausgelöst wird. Durch Schreiben eines Komponententests, der nach einer solchen Ausnahme sucht, können Sie gültige und ungültige Eingaben für die Methode dokumentieren und gleichzeitig überprüfen, ob ungültige Eingaben tatsächlich abgelehnt werden.

Dies ist viel schwieriger, wenn Sie stattdessen Assertions (oder einen anderen Mechanismus) verwenden.

4
lethal-guitar