Ich suche hier eine Empfehlung. Ich habe Probleme damit, ob es besser ist, NULL oder einen leeren Wert von einer Methode zurückzugeben, wenn der Rückgabewert nicht vorhanden ist oder nicht bestimmt werden kann.
Nehmen Sie die folgenden zwei Methoden als Beispiele:
string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID) // finds a Person with a matching personID.
In ReverseString()
würde ich eine leere Zeichenfolge zurückgeben, da der Rückgabetyp Zeichenfolge ist, sodass der Aufrufer dies erwartet. Auf diese Weise müsste der Aufrufer auch nicht prüfen, ob eine NULL zurückgegeben wurde.
In FindPerson()
scheint die Rückkehr von NULL besser zu passen. Unabhängig davon, ob NULL oder ein leeres Personenobjekt ( new Person()
) zurückgegeben wird oder nicht, muss der Aufrufer prüfen, ob das Personenobjekt NULL oder leer ist, bevor er etwas dagegen unternimmt (wie etwa einen Aufruf UpdateName()
). Warum also nicht einfach hier NULL zurückgeben und dann muss der Anrufer nur noch auf NULL prüfen.
Hat jemand anderes damit zu kämpfen? Jede Hilfe oder Einsicht wird geschätzt.
Antworten:
Stackoverflow hat eine gute Diskussion über genau dieses Thema in diesem Q & A . In der am besten bewerteten Frage stellt Kronoz fest:
Persönlich möchte ich leere Zeichenfolgen für Funktionen zurückgeben, die Zeichenfolgen zurückgeben, um den Umfang der Fehlerbehandlung zu minimieren, die eingerichtet werden muss. Sie müssen jedoch sicherstellen, dass die Gruppe, mit der Sie arbeiten, der gleichen Konvention folgt. Andernfalls werden die Vorteile dieser Entscheidung nicht erreicht.
Wie auf dem Poster in der SO-Antwort angegeben, sollten jedoch wahrscheinlich Nullen zurückgegeben werden, wenn ein Objekt erwartet wird, damit kein Zweifel darüber besteht, ob Daten zurückgegeben werden.
Am Ende gibt es keinen einzigen Weg, Dinge am besten zu machen. Die Bildung eines Teamkonsenses wird letztendlich die Best Practices Ihres Teams fördern.
quelle
for x in list_of_things() {...}
ist wohl schneller zu grocken alsl = list_of_things(); if l != null {...}
emit(user.hometown)
alsif user.hometown == null { emit("") else {emit(user.hometown)}
In all dem Code, den ich schreibe, vermeide ich,
null
von einer Funktion zurückzukehren. Ich habe das in Clean Code gelesen .Das Problem bei der Verwendung
null
ist, dass die Person, die die Schnittstelle verwendet, nicht weiß, obnull
ein mögliches Ergebnis vorliegt und ob sie danach suchen muss, da es keinennot null
Referenztyp gibt.In F # können Sie einen
option
Typ zurückgeben, dersome(Person)
oder sein kannnone
, sodass es für den Aufrufer offensichtlich ist, dass er dies überprüfen muss.Das analoge C # (Anti-) Muster ist die
Try...
Methode:Jetzt weiß ich, dass die Leute gesagt haben, sie hassen das
Try...
Muster, weil ein Ausgabeparameter die Ideen einer reinen Funktion bricht, aber es ist wirklich nicht anders als:... und um ehrlich zu sein, können Sie davon ausgehen, dass jeder .NET-Programmierer über das
Try...
Muster Bescheid weiß , da es intern vom .NET-Framework verwendet wird. Das bedeutet, dass sie die Dokumentation nicht lesen müssen, um zu verstehen, was für mich wichtiger ist, als an der puristischen Sichtweise der Funktionen festzuhalten (zu verstehen, dassresult
es sich um einenout
Parameter handelt, nicht um einenref
Parameter).Ich würde also mitmachen,
TryFindPerson
weil Sie anscheinend angeben, dass es völlig normal ist, es nicht zu finden.Wenn es andererseits keinen logischen Grund dafür gibt, dass der Anrufer jemals ein
personId
nicht existierendes bereitstellt , würde ich wahrscheinlich Folgendes tun:... und dann würde ich eine Ausnahme auslösen, wenn sie ungültig wäre. Das
Get...
Präfix impliziert, dass der Anrufer weiß, dass es erfolgreich sein sollte.quelle
Sie können Martin Fowlers Sonderfallmuster aus Paterns of Enterprise Application Architecture ausprobieren :
quelle
Ich würde denken,
ReverseString()
würde die umgekehrte Zeichenfolge zurückgeben und es würde ein werfen,IllegalArgumentException
wenn in a übergebenNull
.Ich denke, Sie
FindPerson()
sollten dem NullObject-Muster folgen oder eine ungeprüfte Ausnahme auslösen , wenn Sie etwas nicht finden, wenn Sie immer in der Lage sein sollten, etwas zu finden.Sich damit auseinandersetzen zu
Null
müssen, sollte vermieden werden. NachdemNull
in Sprachen wurde ein gerufener Milliarden - Dollar - Fehler von ihm Erfinder ist!quelle
Eine Rück Option . Alle Vorteile der Rückgabe eines anderen ungültigen Werts (z. B. das Vorhandensein leerer Werte in Ihren Sammlungen) ohne das Risiko einer NullPointerException.
quelle
boost::optional<T>
Ich sehe beide Seiten dieses Arguments und stelle fest, dass einige ziemlich einflussreiche Stimmen (z. B. Fowler) befürworten, keine Nullen zurückzugeben, um den Code sauber zu halten, zusätzliche Fehlerbehandlungsblöcke zu vermeiden usw.
Ich stehe jedoch eher auf der Seite der Befürworter einer Nullrückgabe. Ich finde, es gibt einen wichtigen Unterschied beim Aufrufen einer Methode, und sie antwortet, wenn ich keine Daten habe, und sie antwortet, wenn ich diese leere Zeichenfolge habe .
Stellen Sie sich das Szenario vor, in dem Sie versuchen, eine Instanz der Klasse nachzuschlagen, da in einigen Abschnitten auf eine Person-Klasse verwiesen wurde. Wenn Sie ein Finder-Attribut (z. B. eine ID) übergeben, kann ein Client sofort nach null suchen, um festzustellen, ob kein Wert gefunden wurde. Dies ist nicht unbedingt außergewöhnlich (daher sind keine Ausnahmen erforderlich), es sollte jedoch auch klar dokumentiert werden. Ja, das erfordert eine gewisse Sorgfalt seitens des Kunden, und nein, ich denke nicht, dass das überhaupt eine schlechte Sache ist.
Betrachten Sie nun die Alternative, bei der Sie ein gültiges Personenobjekt zurückgeben, das nichts enthält. Tragen Sie in alle Werte (Name, Adresse, favoriteDrink) Nullen ein, oder füllen Sie diese jetzt mit gültigen, aber leeren Objekten auf? Wie soll Ihr Kunde nun feststellen, dass keine tatsächliche Person gefunden wurde? Müssen sie überprüfen, ob der Name eine leere Zeichenfolge anstelle von null ist? Wird so etwas nicht tatsächlich zu so viel oder noch mehr Code-Unordnung und bedingten Anweisungen führen, als wenn wir nur auf Null geprüft und weitermachen würden?
Auch hier gibt es Punkte auf beiden Seiten dieses Arguments, denen ich zustimmen könnte, aber ich finde, dass dies für die meisten Leute am sinnvollsten ist (um den Code wartungsfreundlicher zu machen).
quelle
FindPerson
oderGetPerson
eine Null zurückgeben oder eine Ausnahme auslösen , wenn der Schlüssel nicht existiert?“ Als Benutzer der API weiß ich es einfach nicht und muss die Dokumentation überprüfen. Auf der anderen Seitebool TryGetPerson(Guid key, out Person person)
muss ich die Dokumentation nicht überprüfen, und ich weiß, dass keine Ausnahme ausgelöst wird, die einem eher nicht außergewöhnlichen Umstand entspricht. Das ist der Punkt, den ich in meiner Antwort ansprechen möchte.findPerson
) berechnen , ist es völlig normal, dass Sie kein Ergebnis erhalten. Dies sollte nicht zu einer Ausnahme führen.Geben Sie null zurück, wenn Sie wissen möchten, ob das Element vorhanden ist oder nicht. Andernfalls geben Sie den erwarteten Datentyp zurück. Dies gilt insbesondere dann, wenn Sie eine Liste von Artikeln zurücksenden. Normalerweise kann man davon ausgehen, dass der Anrufer, der eine Liste wünscht, die Liste durchlaufen möchte. Viele (die meisten? Alle?) Sprachen schlagen fehl, wenn Sie versuchen, über null zu iterieren, aber nicht, wenn Sie über eine leere Liste iterieren.
Ich finde es frustrierend, Code wie diesen zu verwenden, nur damit er nicht funktioniert:
Ich sollte keinen speziellen Fall für den Fall hinzufügen müssen, wenn es keine Dinge gibt .
quelle
Dies hängt von der Semantik der Methode ab. Sie können möglicherweise angeben, dass Ihre Methode nur "Nicht null" akzeptiert. In Java können Sie dies mit Hilfe einiger Metadaten-Annotationen deklarieren:
Sie sollten den Rückgabewert irgendwie angeben. Wenn Sie deklarieren, dass Sie nur NotNull-Werte akzeptieren, müssen Sie eine leere Zeichenfolge zurückgeben (wenn die Eingabe auch eine leere Zeichenfolge war).
Der zweite Fall ist in seiner Semantik etwas kompliziert. Wenn bei dieser Methode eine Person anhand ihres Primärschlüssels zurückgegeben wird (und erwartet wird, dass die Person existiert), ist es besser, eine Ausnahme auszulösen.
Wenn Sie eine Person anhand einer erratenen ID suchen (was mir seltsam vorkommt), deklarieren Sie diese Methode besser als @Nullable.
quelle
Ich würde empfehlen, wann immer möglich das Null-Objektmuster zu verwenden. Es vereinfacht den Code, wenn Sie Ihre Methoden aufrufen, und Sie können keinen hässlichen Code wie lesen
if (someObject != null && someObject.someMethod () != whateverValue)
Wenn eine Methode beispielsweise eine Auflistung von Objekten zurückgibt, die einem Muster entsprechen, ist die Rückgabe einer leeren Auflistung sinnvoller als die Rückgabe
null
, und das Durchlaufen dieser leeren Auflistung hat praktisch keine Leistungseinbußen. Ein anderer Fall wäre eine Methode, die die zum Protokollieren von Daten verwendete Klasseninstanz zurückgibt und ein Null-Objekt zurückgibt, anstattnull
meiner Meinung nach vorzuziehen, da Benutzer nicht gezwungen werden, immer zu überprüfen, ob die zurückgegebene Referenz istnull
.In Fällen, in denen eine Rückgabe
null
sinnvoll ist (findPerson ()
z. B. Aufrufen der Methode), würde ich versuchen, mindestens eine Methode bereitzustellen, die zurückgibt, wenn das Objekt vorhanden ist (personExists (int personId)
z. B.), ein anderes Beispiel wäre diecontainsKey ()
Methode auf aMap
in Java. Dadurch wird der Code des Anrufers übersichtlicher, da Sie leicht erkennen können, dass das gewünschte Objekt möglicherweise nicht verfügbar ist (Person existiert nicht, Schlüssel ist nicht in der Karte vorhanden). Die ständige Überprüfung, ob eine Referenznull
verschleiert ist, verschleiert meiner Meinung nach den Code.quelle
null ist die beste Sache, wenn und nur wenn die folgenden Bedingungen zutreffen:
Stellen Sie außerdem sicher, dass Sie die Tatsache dokumentieren, dass die Funktion null zurückgeben kann.
Wenn Sie diese Regeln befolgen, sind Nullen meist harmlos. Beachten Sie, dass Sie normalerweise sofort eine NullPointerException erhalten, wenn Sie vergessen, auf Null zu prüfen. Dies ist normalerweise ein recht einfach zu behebender Fehler. Dieser Fail-Fast-Ansatz ist viel besser als ein gefälschter Rückgabewert (z. B. eine leere Zeichenfolge), der sich im Hintergrund auf Ihrem System ausbreitet und möglicherweise Daten beschädigt, ohne eine Ausnahme auszulösen.
Wenn Sie diese Regeln schließlich auf die beiden in der Frage aufgeführten Funktionen anwenden, gilt Folgendes:
quelle
Meiner Meinung nach gibt es einen Unterschied zwischen der Rückgabe von NULL, der Rückgabe eines leeren Ergebnisses (z. B. der leeren Zeichenfolge oder einer leeren Liste) und dem Auslösen einer Ausnahme.
Normalerweise gehe ich folgendermaßen vor. Ich betrachte eine Funktion oder Methode f (v1, ..., vn) als die Anwendung einer Funktion
Dabei ist S der "Zustand der Welt" T1, ..., Tn die Art der Eingabeparameter und T der Rückgabetyp.
Ich versuche zuerst, diese Funktion zu definieren. Wenn die Funktion partiell ist (dh es gibt einige Eingabewerte, für die sie nicht definiert ist), gebe ich NULL zurück, um dies zu signalisieren. Dies liegt daran, dass die Berechnung normal beendet werden soll und ich weiß, dass die von mir angeforderte Funktion für die angegebenen Eingaben nicht definiert ist. Die Verwendung einer leeren Zeichenfolge als Rückgabewert ist beispielsweise nicht eindeutig, da die Funktion möglicherweise an den Eingängen definiert ist und die leere Zeichenfolge das richtige Ergebnis darstellt.
Ich denke, die zusätzliche Prüfung für einen NULL-Zeiger im aufrufenden Code ist notwendig, weil Sie eine Teilfunktion anwenden und es die Aufgabe der aufgerufenen Methode ist, Ihnen mitzuteilen, ob die Funktion für die angegebene Eingabe nicht definiert ist.
Ich bevorzuge Ausnahmen für Fehler, bei denen die Berechnung nicht möglich ist (dh es konnte keine Antwort gefunden werden).
Angenommen, ich habe eine Klasse Customer und möchte eine Methode implementieren
in der Anwendungsdatenbank nach einem Kunden anhand seines Codes suchen. Bei dieser Methode würde ich
Das Extra prüft auf null, z
sind Teil der Semantik dessen, was ich tue, und ich würde sie nicht einfach "überspringen", um den Code besser lesbar zu machen. Ich halte es nicht für eine gute Praxis, die Semantik des vorliegenden Problems zu vereinfachen, nur um den Code zu vereinfachen.
Natürlich ist es gut, wenn die Sprache eine spezielle Syntax unterstützt, da die Überprüfung auf Null sehr häufig auftritt.
Ich würde auch erwägen, das Nullobjektmuster (wie von Laf vorgeschlagen) zu verwenden, solange ich das Nullobjekt einer Klasse von allen anderen Objekten unterscheiden kann.
quelle
Methoden, die Auflistungen zurückgeben, sollten leer zurückgeben, andere könnten jedoch null zurückgeben, da es bei den Auflistungen vorkommen kann, dass Sie ein Objekt haben, das jedoch keine Elemente enthält, sodass der Aufrufer nur die Größe und nicht beide überprüft.
quelle
Für mich gibt es hier zwei Fälle. Wenn Sie eine Liste zurücksenden, sollten Sie die Liste immer zurücksenden, egal was passiert. Leer, wenn Sie nichts zu tun haben.
Der einzige Fall, in dem ich eine Debatte sehe, ist, wenn Sie einen einzelnen Artikel zurücksenden. Ich bevorzuge es, im Falle eines Fehlers in einer solchen Situation null zurückzugeben, weil die Dinge schnell scheitern.
Wenn Sie ein Nullobjekt zurückgeben und der Aufrufer ein reales Objekt benötigt, versucht er möglicherweise, das Nullobjekt zu verwenden, was zu unerwartetem Verhalten führt. Ich denke, es ist besser für die Routine, einen Boom zu erleben, wenn sie vergessen, mit der Situation umzugehen. Wenn etwas nicht stimmt, möchte ich so schnell wie möglich eine Ausnahme. Es ist weniger wahrscheinlich, dass Sie einen Bug auf diese Weise versenden.
quelle
Hinzufügen zu dem, was bereits über den Typkonstruktor gesagt wurde
Maybe
:Ein weiterer großer Vorteil, abgesehen davon, dass man keine NPEs riskiert, ist der semantische. In der funktionalen Welt, in der wir uns mit reinen Funktionen befassen, möchten wir versuchen, Funktionen zu erstellen, deren Anwendung genau ihrem Rückgabewert entspricht . Betrachten Sie den Fall von
String.reverse()
: Dies ist eine Funktion, bei der eine bestimmte Zeichenfolge die umgekehrte Version dieser Zeichenfolge darstellt. Wir wissen, dass diese Zeichenfolge vorhanden ist, da jede Zeichenfolge umgekehrt werden kann (Zeichenfolgen sind im Grunde geordnete Zeichensätze).Was ist nun mit
findCustomer(int id)
? Diese Funktion repräsentiert den Kunden, der die angegebene ID hat. Dies ist etwas, was existieren kann oder nicht. Denken Sie jedoch daran, dass Funktionen nur einen Rückgabewert haben. Sie können also nicht wirklich sagen, dass diese Funktion einen Kunden mit der angegebenen ID zurückgibt ODER dass sie zurückgibtnull
.Dies ist mein Hauptproblem sowohl bei der Rückgabe
null
als auch bei der Rückgabe eines Null-Objekts. Sie sind irreführend.null
ist kein Kunde und es ist auch nicht wirklich "die Abwesenheit eines Kunden". Es ist die Abwesenheit von etwas. Es ist nur ein typloser Hinweis auf NICHTS. Sehr leise und sehr hässlich und sehr fehleranfällig. Ein Null-Objekt ist auch hier meiner Meinung nach ziemlich irreführend. Ein Null-Kunde IST ein Kunde. Es ist nicht die Abwesenheit eines Kunden, es ist nicht der Kunde mit dem gewünschten Ausweis. Dies ist NICHT der Kunde, nach dem ich gefragt habe, aber Sie behaupten immer noch, Sie hätten einen Kunden zurückgegeben. Es hat die falsche ID, das ist eindeutig ein Fehler. Es bricht den durch den Methodennamen und die Unterschrift vorgeschlagenen Vertrag. Der Client-Code muss Informationen zu Ihrem Design enthalten oder die Dokumentation lesen.Beide Probleme werden durch a gelöst
Maybe Customer
. Plötzlich sagen wir nicht "Diese Funktion repräsentiert den Kunden mit der angegebenen ID". Wir sagen: "Diese Funktion repräsentiert den Kunden mit der angegebenen ID, wenn sie existiert . Es ist jetzt sehr klar und deutlich, was sie tut. Wenn Sie nach dem Kunden mit einer nicht vorhandenen ID fragen, erhalten SieNothing
. Wie ist das besser?" alsnull
? nicht nur für die NPE Risiko. Aber auch , weil der TypNothing
ist nicht nurMaybe
. Es istMaybe Customer
. Es semantische Bedeutung hat. Es „das Fehlen eines Kunden“ bedeutet insbesondere, was darauf hinweist , dass ein solcher Kunde nicht existiert.Ein weiteres Problem mit null ist natürlich, dass es mehrdeutig ist. Ist es so, dass Sie den Kunden nicht gefunden haben, oder gab es einen DB-Verbindungsfehler oder darf ich diesen Kunden einfach nicht sehen?
Wenn Sie mehrere Fehlerfälle wie diesen behandeln müssen, können Sie entweder Ausnahmen für diese Versionen auslösen oder (und ich bevorzuge dies) eine zurückgeben
Either CustomerLoadError Customer
, die entweder einen Fehler oder den zurückgegebenen Kunden darstellt. Es bietet alle VorteileMaybe Customer
und ermöglicht es Ihnen, anzugeben, was schief gehen kann.Das Wichtigste, wonach ich strebe, ist, den Vertrag der Funktion in ihrer Unterschrift festzuhalten. Gehen Sie nicht von Dingen aus, verlassen Sie sich nicht auf flüchtige Konventionen, seien Sie explizit.
quelle
1) Wenn die Semantik der Funktion ist, dass sie nichts zurückgeben kann, MUSS der Aufrufer sie testen, sonst wird er z. Nimm dein Geld und gib es niemandem.
2) Es ist gut, Funktionen zu erstellen, die semantisch immer etwas zurückgeben (oder werfen).
Bei einem Logger ist es sinnvoll, einen Logger zurückzugeben, der nicht protokolliert, wenn kein Logger definiert wurde. Bei der Rückgabe von Auflistungen ist es fast nie sinnvoll (außer auf "niedrig genug", wo die Auflistung selbst DIE Daten ist, nicht das, was sie enthält), nichts zurückzugeben, da eine leere Menge eine Menge ist, nicht nichts.
Mit einem Personenbeispiel würde ich den Weg des "Ruhezustands" gehen (Get vs Load, IIRC, aber dort wird es durch Proxy-Objekte zum verzögerten Laden kompliziert), zwei Funktionen zu haben, eine, die null zurückgibt, und eine, die wirft.
quelle
NULL sollte zurückgegeben werden, wenn die Anwendung erwartet, dass Daten verfügbar sind, diese jedoch nicht verfügbar sind. Beispielsweise sollte ein Dienst, der die CITY basierend auf der Postleitzahl zurückgibt, null zurückgeben, wenn der Ort nicht gefunden wird. Der Anrufer kann dann entscheiden, ob er mit der Null umgehen oder in die Luft jagen möchte.
Eine leere Liste sollte zurückgegeben werden, wenn zwei Möglichkeiten bestehen. Daten verfügbar oder KEINE Daten verfügbar. Zum Beispiel ein Dienst, der die STADT (en) zurückgibt, wenn die Population eine bestimmte Anzahl überschreitet. Es kann eine leere Liste zurückgegeben werden, wenn keine Daten vorhanden sind, die den angegebenen Kriterien entsprechen.
quelle
Returning NULL ist ein schreckliches Design in einer objektorientierten Welt. Kurz gesagt, die Verwendung von NULL führt zu:
In diesem Blog-Beitrag finden Sie eine ausführliche Erklärung: http://www.yegor256.com/2014/05/13/why-null-is-bad.html
quelle