it-swarm.com.de

Sollte ich leere Sammlungen in meinen Methoden akzeptieren, die über sie iterieren?

Ich habe eine Methode, bei der die gesamte Logik in einer foreach-Schleife ausgeführt wird, die über den Parameter der Methode iteriert:

public IEnumerable<TransformedNode> TransformNodes(IEnumerable<Node> nodes)
{
    foreach(var node in nodes)
    {
        // yadda yadda yadda
        yield return transformedNode;
    }
}

In diesem Fall führt das Senden einer leeren Sammlung zu einer leeren Sammlung, aber ich frage mich, ob dies unklug ist.

Meine Logik hier ist, dass wenn jemand diese Methode aufruft, er beabsichtigt, Daten zu übergeben, und nur unter fehlerhaften Umständen eine leere Sammlung an meine Methode übergeben würde.

Sollte ich dieses Verhalten abfangen und eine Ausnahme auslösen, oder empfiehlt es sich, die leere Sammlung zurückzugeben?

40
Nick Udell

Dienstprogrammmethoden sollten keine leeren Sammlungen auslösen. Ihre API-Clients würden Sie dafür hassen.

Eine Sammlung kann leer sein. Eine "Sammlung, die nicht leer sein darf" ist konzeptionell viel schwieriger zu bearbeiten.

Das Transformieren einer leeren Sammlung hat ein offensichtliches Ergebnis: die leere Sammlung. (Sie können sogar Müll sparen, indem Sie den Parameter selbst zurückgeben.)

Es gibt viele Umstände, unter denen ein Modul Listen von Dingen führt, die möglicherweise bereits mit etwas gefüllt sind oder nicht. Vor jedem Aufruf von transform auf Leere prüfen zu müssen, ist ärgerlich und kann einen einfachen, eleganten Algorithmus in ein hässliches Chaos verwandeln.

Gebrauchsmuster sollten immer danach streben, in ihren Inputs liberal und in ihren Outputs konservativ zu sein.

Behandeln Sie aus all diesen Gründen, um Gottes willen, die leere Sammlung korrekt. Nichts ist ärgerlicher als ein Hilfsmodul, das denkt weiß, was Sie besser wollen als Sie.

175
Kilian Foth

Ich sehe zwei wichtige Fragen, die die Antwort darauf bestimmen:

  1. Kann Ihre Funktion etwas Sinnvolles und Logisches zurückgeben, wenn eine leere Sammlung übergeben wird (einschließlich null )?
  2. Was ist der allgemeine Programmierstil in dieser Anwendung/Bibliothek/diesem Team? (Insbesondere, wie FP bist du?)

1. Sinnvolle Rendite

Wenn Sie etwas Sinnvolles zurückgeben können, lösen Sie im Wesentlichen keine Ausnahme aus. Lassen Sie den Anrufer mit dem Ergebnis umgehen. Also wenn deine Funktion ...

  • Zählt die Anzahl der Elemente in der Sammlung und gibt 0 zurück. Das war einfach.
  • Sucht nach Elementen, die bestimmten Kriterien entsprechen, und gibt eine leere Sammlung zurück. Bitte werfen Sie nichts. Der Anrufer verfügt möglicherweise über eine große Sammlung von Sammlungen, von denen einige leer sind, andere nicht. Der Aufrufer möchte alle passenden Elemente in jeder Sammlung. Ausnahmen erschweren dem Anrufer nur das Leben.
  • Sucht nach den größten/kleinsten/am besten zu den Kriterien passenden Kriterien in der Liste. Hoppla. Abhängig von der Stilfrage können Sie hier eine Ausnahme auslösen oder null zurückgeben. Ich hasse null (sehr eine FP Person)), aber es ist hier wohl sinnvoller und Sie können Ausnahmen für unerwartete Fehler in Ihrem eigenen Code reservieren. Wenn der Anrufer nicht nach null sucht, es wird sowieso eine ziemlich klare Ausnahme geben. Aber das überlassen Sie ihnen.
  • Fragt nach dem n-ten Element in der Sammlung oder den ersten/letzten n Elementen. Dies ist der bisher beste Fall für eine Ausnahme und derjenige, der am wenigsten Verwirrung und Schwierigkeiten für den Anrufer verursacht. Es kann immer noch ein Fall für null gemacht werden, wenn Sie und Ihr Team es gewohnt sind, dies aus allen im vorherigen Punkt angegebenen Gründen zu überprüfen, aber dies ist der bisher gültigste Fall für das Auslösen einer DudeYou Know YouShouldCheckTheSizeFirst - Ausnahme. Wenn Ihr Stil funktionaler ist, dann entweder null oder lesen Sie weiter zu meinem Stil Antworten.

Im Allgemeinen sagt mir meine FP Voreingenommenheit), dass ich etwas Sinnvolles zurückgeben soll, und null kann in diesem Fall eine gültige Bedeutung haben.

2. Stil

Begünstigt Ihr allgemeiner Code (oder der Code des Projekts oder des Teams) einen funktionalen Stil? Wenn nein, werden Ausnahmen erwartet und behandelt. Wenn ja, sollten Sie ein Optionstyp zurückgeben. Bei einem Optionstyp geben Sie entweder eine aussagekräftige Antwort oder Keine/Nichts zurück. In meinem dritten Beispiel von oben wäre Nichts eine gute Antwort in FP Stil. Die Tatsache, dass die Funktion zurückgibt Ein Optionstyp signalisiert dem Anrufer eindeutig, dass eine aussagekräftige Antwort möglicherweise nicht möglich ist und dass der Anrufer darauf vorbereitet sein sollte. Ich bin der Meinung, dass er dem Anrufer mehr Optionen bietet (wenn Sie das Wortspiel verzeihen).

In F # machen all die coolen .Net-Kids so etwas, aber C # machtnterstützung diesen Stil.

tl; dr

Behalten Sie Ausnahmen für unerwartete Fehler in Ihrem eigenen Codepfad bei, nicht vollständig vorhersehbare (und legale) Eingaben von anderen.

26
itsbruce

Wie immer kommt es darauf an.

Ist es wichtig, dass die Sammlung leer ist?
Der meiste Code für die Sammlungsbearbeitung würde wahrscheinlich "Nein" sagen. Eine Sammlung kann eine beliebige Anzahl Elemente enthalten, einschließlich Null.

Wenn Sie eine Sammlung haben, in der es "ungültig" ist, keine Elemente darin zu haben, ist dies eine neue Anforderung, und Sie müssen entscheiden, was Sie dagegen tun möchten.

Leihen Sie sich eine Testlogik aus der Datenbankwelt aus: Testen Sie auf null Elemente, eins Element und zwei Elemente. Dies ist für die wichtigsten Fälle geeignet (Ausspülen von schlecht geformten inneren oder kartesischen Verbindungsbedingungen).

18
Phill W.

Akzeptieren Sie aus Gründen des guten Designs so viele Variationen Ihrer Eingaben wie möglich oder praktisch. Ausnahmen sollten nur ausgelöst werden, wenn (nicht akzeptable Eingaben angezeigt werden OR unerwartete Fehler bei der Verarbeitung auftreten) AND Das Programm kann daher nicht vorhersehbar fortgesetzt werden.

In diesem Fall sollte erwartet werden, dass eine leere Sammlung angezeigt wird, und Ihr Code muss damit umgehen (was er bereits tut). Es wäre eine Verletzung von allem, was gut ist, wenn Ihr Code hier eine Ausnahme auslösen würde. Dies wäre vergleichbar mit dem Multiplizieren von 0 mit 0 in der Mathematik. Es ist redundant, aber absolut notwendig, damit es so funktioniert, wie es funktioniert.

Nun zum Argument der Nullsammlung. In diesem Fall ist eine Nullsammlung ein Programmierfehler: Der Programmierer hat vergessen, eine Variable zuzuweisen. Dies ist ein Fall, in dem eine Ausnahme ausgelöst werden könnte, da Sie diese nicht sinnvoll in eine Ausgabe umwandeln können und der Versuch, dies zu tun, zu unerwartetem Verhalten führen würde. Dies wäre vergleichbar mit einer Division durch Null in der Mathematik - es ist völlig bedeutungslos.

11
theMayer

Die richtige Lösung ist viel schwieriger zu erkennen, wenn Sie Ihre Funktion nur isoliert betrachten. Betrachten Sie Ihre Funktion als einen Teil von ein größeres Problem . Eine mögliche Lösung für dieses Beispiel sieht folgendermaßen aus (in Scala):

input.split("\\D")
.filterNot (_.isEmpty)
.map (_.toInt)
.filter (x => x >= 1000 && x <= 9999)

Zuerst teilen Sie die Zeichenfolge nach Ziffern auf, filtern die leeren Zeichenfolgen heraus, konvertieren die Zeichenfolgen in Ganzzahlen und filtern dann, um nur die vierstelligen Zahlen beizubehalten. Ihre Funktion könnte die map (_.toInt) in der Pipeline sein.

Dieser Code ist ziemlich einfach, da jede Stufe in der Pipeline nur eine leere Zeichenfolge oder eine leere Sammlung verarbeitet. Wenn Sie am Anfang eine leere Zeichenfolge eingeben, wird am Ende eine leere Liste angezeigt. Sie müssen nicht nach jedem Aufruf anhalten und nach null oder einer Ausnahme suchen.

Dies setzt natürlich voraus, dass eine leere Ausgabeliste nicht mehr als eine Bedeutung hat. Wenn Sie zwischen einer leeren Ausgabe, die durch eine leere Eingabe verursacht wird, und einer, die durch die Transformation selbst verursacht wird, unterscheiden müssen, ändert dies die Dinge vollständig.

10
Karl Bielefeldt

Bei dieser Frage geht es eigentlich um Ausnahmen. Wenn Sie es so betrachten und die leere Sammlung als Implementierungsdetail ignorieren, ist die Antwort einfach:

1) Eine Methode sollte eine Ausnahme auslösen, wenn sie nicht fortfahren kann: Entweder kann sie die angegebene Aufgabe nicht ausführen oder sie gibt den entsprechenden Wert zurück.

2) Eine Methode sollte eine Ausnahme abfangen, wenn sie trotz des Fehlers fortgesetzt werden kann.

Daher sollte Ihre Hilfsmethode nicht "hilfreich" sein und eine Ausnahme auslösen, es sei denn, it kann ihre Aufgabe nicht mit einer leeren Sammlung ausführen. Lassen Sie den Anrufer bestimmen, ob die Ergebnisse verarbeitet werden können.

Ob es eine leere Sammlung oder null zurückgibt, ist etwas schwieriger, aber nicht viel: nullfähige Sammlungen sollten nach Möglichkeit vermieden werden. Der Zweck einer nullbaren Sammlung besteht darin, (wie in SQL) anzuzeigen, dass Sie nicht über die Informationen verfügen. Eine Sammlung von untergeordneten Elementen kann beispielsweise null sein, wenn Sie nicht wissen, ob jemand welche hat, dies jedoch nicht weiß, dass sie es nicht tun. Aber wenn das aus irgendeinem Grund wichtig ist, ist es wahrscheinlich eine zusätzliche Variable wert, um es zu verfolgen.

2
jmoreno

Die Methode heißt TransformNodes. Im Falle einer leeren Sammlung als Eingabe ist das Zurückholen einer leeren Sammlung natürlich und intuitiv und macht einen perfekten mathematischen Sinn.

Wenn die Methode Max heißt und das maximale Element zurückgeben soll, wäre es natürlich, NoSuchElementException auf eine leere Sammlung zu werfen, da das Maximum von nichts keinen mathematischen Sinn ergibt.

Wenn die Methode JoinSqlColumnNames heißt und eine Zeichenfolge zurückgeben soll, bei der die Elemente durch ein Komma verbunden sind, um sie in SQL-Abfragen zu verwenden, ist es sinnvoll, IllegalArgumentException auf eine leere Sammlung als zu werfen Der Aufrufer würde schließlich ohnehin einen SQL-Fehler erhalten, wenn er die Zeichenfolge in einer SQL-Abfrage direkt ohne weitere Überprüfungen verwenden würde. Statt nach einer zurückgegebenen leeren Zeichenfolge zu suchen, hätte er stattdessen tatsächlich nach einer leeren Sammlung suchen sollen.

1
janos

Lassen Sie uns einen Schritt zurücktreten und ein anderes Beispiel verwenden, das das arithmetische Mittel eines Array von Werten berechnet.

Wenn das Eingabearray leer (oder null) ist, können Sie die Anforderung des Anrufers angemessen erfüllen? Was sind Ihre Optionen? Nun, Sie könnten:

  • einen Fehler präsentieren/zurückgeben/werfen. Verwenden der Konvention Ihrer Codebasis für diese Fehlerklasse.
  • dokumentieren, dass ein Wert wie Null zurückgegeben wird
  • dokumentieren, dass ein bestimmter ungültiger Wert zurückgegeben wird (z. B. NaN)
  • dokumentieren, dass ein magischer Wert zurückgegeben wird (z. B. ein Min oder Max für den Typ oder ein hoffentlich indikativer Wert)
  • erklären Sie, dass das Ergebnis nicht angegeben ist
  • deklarieren Sie, dass die Aktion undefiniert ist
  • usw.

Ich sage, gib ihnen den Fehler, wenn sie dir eine ungültige Eingabe gegeben haben und die Anfrage nicht abgeschlossen werden kann. Ich meine einen schweren Fehler vom ersten Tag an, damit sie die Anforderungen Ihres Programms verstehen. Schließlich ist Ihre Funktion nicht in der Lage zu reagieren. Wenn die Operation könnte fehlschlägt (z. B. eine Datei kopieren), sollte Ihre API ihnen einen Fehler geben, mit dem sie umgehen können.

So können Sie festlegen, wie Ihre Bibliothek fehlerhafte und möglicherweise fehlgeschlagene Anforderungen verarbeitet.

Es ist sehr wichtig, dass Ihr Code konsistent mit diesen Fehlerklassen umgeht.

Die nächste Kategorie besteht darin, zu entscheiden, wie Ihre Bibliothek mit Unsinnanforderungen umgeht. Zurück zu einem ähnlichen Beispiel: Verwenden Sie eine Funktion, die bestimmt, ob eine Datei in einem Pfad vorhanden ist: bool FileExistsAtPath(String). Wie gehen Sie mit diesem Szenario um, wenn der Client eine leere Zeichenfolge übergibt? Wie wäre es mit einem leeren oder Null-Array, das an void SaveDocuments(Array<Document>) übergeben wird? Entscheide dich für deine Bibliothek/Codebasis und sei konsistent. Ich betrachte diese Fälle zufällig als Fehler und verbiete Kunden, unsinnige Anfragen zu stellen, indem ich sie als Fehler kennzeichne (über eine Behauptung). Einige Leute werden sich dieser Idee/Aktion stark widersetzen. Ich finde diese Fehlererkennung sehr hilfreich. Es ist sehr gut geeignet, um Probleme in Programmen zu lokalisieren - mit einer guten Lokalität für das betreffende Programm. Programme sind viel klarer und korrekter (berücksichtigen Sie die Entwicklung Ihrer Codebasis) und brennen keine Zyklen innerhalb von Funktionen, die nichts bewirken. Der Code ist auf diese Weise kleiner/sauberer, und die Überprüfungen werden im Allgemeinen an die Stellen verschoben, an denen das Problem auftreten kann.

0
justin