wake-up-neo.com

Sollten Funktionen null oder ein leeres Objekt zurückgeben?

Was ist das Best Practice bei der Rückgabe von Daten aus Funktionen. Ist es besser ein Null oder ein leeres Objekt zurückzugeben? Und warum sollte man übereinander tun?

Bedenken Sie:

public UserEntity GetUserById(Guid userId)
{
     //Imagine some code here to access database.....

     //Check if data was returned and return a null if none found
     if (!DataExists)
        return null; 
        //Should I be doing this here instead? 
        //return new UserEntity();  
     else
        return existingUserEntity;
}

Nehmen wir an, es gäbe gültige Fälle in diesem Programm, in denen keine Benutzerinformationen in der Datenbank mit dieser GUID vorhanden wären. Ich würde mir vorstellen, dass es in diesem Fall nicht angebracht wäre, eine Ausnahme zu werfen? Außerdem habe ich den Eindruck, dass die Ausnahmebehandlung die Leistung beeinträchtigen kann.

210
7wp

Die Rückgabe von null ist normalerweise die beste Idee, wenn Sie angeben möchten, dass keine Daten verfügbar sind.

Ein leeres Objekt impliziert, dass Daten zurückgegeben wurden, während die Rückgabe von null eindeutig anzeigt, dass nichts zurückgegeben wurde.

Wenn Sie versuchen, auf Mitglieder im Objekt zuzugreifen, führt die Rückgabe einer Null außerdem zu einer Null-Ausnahme. Dies kann nützlich sein, um fehlerhaften Code hervorzuheben. Der Versuch, auf ein Mitglied von nothing zuzugreifen, macht keinen Sinn. Der Zugriff auf Mitglieder eines leeren Objekts schlägt nicht fehl, was bedeutet, dass Fehler unentdeckt bleiben können.

208
ljs

Es kommt darauf an, was für Ihren Fall am sinnvollsten ist.

Ist es sinnvoll, null zurückzugeben, z. msgstr "kein solcher Benutzer existiert"?

Oder ist es sinnvoll, einen Standardbenutzer anzulegen? Dies ist am sinnvollsten, wenn Sie davon ausgehen können, dass der aufrufende Code vorsieht, dass ein Benutzer existiert, wenn er danach fragt, wenn er nicht existiert.

Oder ist es sinnvoll, eine Ausnahme (a la "FileNotFound") auszulösen, wenn der aufrufende Code einen Benutzer mit einer ungültigen ID fordert?

Vom Standpunkt der Trennung von Bedenken/SRP sind die ersten beiden jedoch korrekter. Und technisch das erste ist das richtigste (aber nur haarscharf) - GetUserById sollte nur für eine Sache verantwortlich sein - den Benutzer zu bekommen. Die Behandlung eines eigenen "Benutzer existiert nicht" -Falls durch Rückgabe von etwas anderem könnte eine Verletzung von SRP darstellen. Eine Unterteilung in ein anderes Häkchen - bool DoesUserExist(id) wäre angebracht, wenn Sie eine Ausnahme auslösen möchten.

Basierend auf ausführlichen Kommentaren unten: Wenn dies eine Entwurfsfrage auf API-Ebene ist, kann diese Methode analog zu "OpenFile" oder "ReadEntireFile" sein. Wir "öffnen" einen Benutzer aus einem Repository und hydratisieren das Objekt aus den resultierenden Daten. In diesem Fall kann eine Ausnahme angebracht sein . Es könnte nicht sein, aber es könnte sein.

Alle Ansätze sind akzeptabel - es kommt nur auf den größeren Kontext der API/Anwendung an.

44
Rex M

Persönlich verwende ich NULL. Es wird deutlich, dass keine Daten zurückgegeben werden müssen. Es gibt jedoch Fälle, in denen ein Null-Objekt nützlich sein kann.

30
Fernando

Wenn Ihr Rückgabetyp ein Array ist, geben Sie ein leeres Array zurück, andernfalls geben Sie null zurück.

27
Darin Dimitrov

Sie sollten (nur) eine Ausnahme auslösen, wenn ein bestimmter Vertrag gebrochen ist.
In Ihrem speziellen Beispiel hängt es von der Tatsache ab, ob fehlende (gelöschte) Benutzer ein erwarteter Fall sind, wenn Sie nach einer UserEntity auf der Basis einer bekannten ID fragen. Wenn dies der Fall ist, geben Sie null zurück. Wenn dies jedoch kein erwarteter Fall ist, lösen Sie eine Ausnahme aus.
Beachten Sie, dass die Funktion beim Aufruf von UserEntity GetUserByName(string name) wahrscheinlich nicht werfen, sondern null zurückgeben würde. In beiden Fällen wäre die Rückgabe einer leeren UserEntity nicht hilfreich.

Bei Strings, Arrays und Collections ist die Situation normalerweise anders. Ich erinnere mich an einige Richtlinien aus MS, bei denen Methoden null als "leere" Liste akzeptieren sollten, aber Sammlungen mit einer Länge von Null anstelle von null zurückgeben sollten. Das gleiche gilt für Streicher. Beachten Sie, dass Sie leere Arrays deklarieren können: int[] arr = new int[0];

12
Henk Holterman

Dies ist eine geschäftliche Frage, die davon abhängt, ob die Existenz eines Benutzers mit einer bestimmten Guid-ID ein erwarteter normaler Anwendungsfall für diese Funktion ist oder eine Anomalie, die die Anwendung daran hindert, die von dieser Methode bereitgestellte Funktion erfolgreich abzuschließen Einspruch erheben gegen ...

Wenn es sich um eine "Ausnahme" handelt, verhindert die Abwesenheit eines Benutzers mit dieser ID, dass die Anwendung die von ihr ausgeführte Funktion erfolgreich ausführt. (Angenommen, wir erstellen eine Rechnung für einen Kunden, an den wir das Produkt geliefert haben.) ), sollte diese Situation eine ArgumentException (oder eine andere benutzerdefinierte Ausnahme) auslösen.

Wenn ein fehlender Benutzer in Ordnung ist (eines der möglichen normalen Ergebnisse des Aufrufs dieser Funktion), wird eine Null zurückgegeben ....

BEARBEITEN: (um den Kommentar von Adam in einer anderen Antwort zu adressieren)

Wenn die Anwendung mehrere Geschäftsprozesse enthält, von denen einer oder mehrere einen Benutzer benötigen, um erfolgreich abgeschlossen zu werden, und einer oder mehrere ohne Benutzer erfolgreich abgeschlossen werden können, sollte die Ausnahme weiter oben in der Aufrufliste ausgelöst werden, näher an der Position Die Geschäftsprozesse, die einen Benutzer erfordern, rufen diesen Ausführungsthread auf. Methoden zwischen dieser Methode und dem Punkt (an dem die Ausnahme ausgelöst wird) sollten lediglich mitteilen, dass kein Benutzer vorhanden ist (null, boolesch, was auch immer - dies ist ein Implementierungsdetail).

Aber wenn alle Prozesse in der Anwendung erfordern einen Benutzer, würde ich immer noch die Ausnahme in dieser Methode auslösen ...

11
Charles Bretana

Ich persönlich würde null zurückgeben, da ich davon ausgehen würde, dass sich die DAL/Repository-Schicht so verhält.

Wenn es nicht existiert, geben Sie nichts zurück, was als erfolgreiches Abrufen eines Objekts angesehen werden könnte. null funktioniert hier hervorragend.

Das Wichtigste ist, dass Sie in Ihrer DAL/Repos-Ebene konsistent sind, damit Sie nicht verwirrt werden, wie Sie sie verwenden.

10
Alex Moore

Ich neige dazu

  • return null wenn die Objekt-ID nicht existiert, wenn vorher nicht bekannt ist, ob sie sollte existiert.
  • throw wenn die Objekt-ID nicht existiert, wenn sie sollte existiert.

Ich unterscheide diese beiden Szenarien mit diesen drei Arten von Methoden. Zuerst:

Boolean TryGetSomeObjectById(Int32 id, out SomeObject o)
{
    if (InternalIdExists(id))
    {
        o = InternalGetSomeObject(id);

        return true;
    }
    else
    {
        return false;
    }
}

Zweite:

SomeObject FindSomeObjectById(Int32 id)
{
    SomeObject o;

    return TryGetObjectById(id, out o) ? o : null;
}

Dritte:

SomeObject GetSomeObjectById(Int32 id)
{
    SomeObject o;

    if (!TryGetObjectById(id, out o))
    {
        throw new SomeAppropriateException();
    }

    return o;
}
7
Johann Gerell

Ein weiterer Ansatz besteht darin, ein Callback-Objekt oder einen Delegaten zu übergeben, der den Wert verarbeitet. Wenn ein Wert nicht gefunden wird, wird der Rückruf nicht aufgerufen.

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

Dies funktioniert gut, wenn Sie Nullprüfungen im gesamten Code vermeiden möchten und wenn das Nichtfinden eines Werts kein Fehler ist. Sie können auch einen Rückruf für den Fall einrichten, dass keine Objekte gefunden werden, wenn Sie eine spezielle Verarbeitung benötigen.

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

Der gleiche Ansatz mit einem einzelnen Objekt könnte folgendermaßen aussehen:

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

Aus gestalterischer Sicht gefällt mir dieser Ansatz sehr gut, aber ich habe den Nachteil, dass die Call-Site in Sprachen umfangreicher wird, die erstklassige Funktionen nicht ohne weiteres unterstützen.

6
Marc

Ich bevorzuge null, da es mit dem Null-Koaleszenz-Operator (??).

4
Andrew Medico

Ich würde sagen, dass anstelle eines leeren Objekts null zurückgegeben wird.

In der spezifischen Instanz, die Sie hier erwähnt haben, suchen Sie einen Benutzer anhand der Benutzer-ID. Dies ist eine Art Schlüssel für diesen Benutzer. In diesem Fall möchte ich wahrscheinlich eine Ausnahme auslösen, wenn keine Benutzerinstanzinstanz gefunden wird .

Dies ist die Regel, die ich im Allgemeinen befolge:

  • Wenn bei einer Suche mit dem Primärschlüssel kein Ergebnis gefunden wird, lösen Sie die ObjectNotFoundException aus.
  • Wenn bei einem Suchvorgang nach anderen Kriterien kein Ergebnis gefunden wird, geben Sie null zurück.
  • Wenn bei einem Suchvorgang nach einem Nichtschlüsselkriterium kein Ergebnis gefunden wird, das möglicherweise mehrere Objekte zurückgibt, wird eine leere Auflistung zurückgegeben.
4

Wir verwenden CSLA.NET und es wird die Ansicht vertreten, dass ein fehlgeschlagener Datenabruf ein "leeres" Objekt zurückgeben sollte. Das ist eigentlich ziemlich ärgerlich, da es die Konvention verlangt, zu prüfen, ob obj.IsNew rathern als obj == null.

Wie in einem vorherigen Poster bereits erwähnt, führt Nullrückgabewerte dazu, dass Code sofort fehlschlägt, wodurch die Wahrscheinlichkeit von Stealth-Problemen durch leere Objekte verringert wird.

Persönlich finde ich null eleganter.

Es ist ein sehr häufiger Fall, und ich bin überrascht, dass die Leute hier überrascht zu sein scheinen: In jeder Webanwendung werden Daten häufig mit einem Querystring-Parameter abgerufen, der offensichtlich entstellt werden kann. Daher muss der Entwickler Vorkommnisse von "Nicht gefunden" behandeln ".

Sie könnten damit umgehen, indem Sie:

 if (User.Exists (id)) {
 this.User = User.Fetch (id); 
} else {
 Response.Redirect ("~ /notfound.aspx");[.____.}

... aber das ist jedes Mal ein zusätzlicher Aufruf der Datenbank, was auf stark frequentierten Seiten ein Problem sein kann. Wohingegen:

 this.User = User.Fetch (id); 
 
 if (this.User == null) {
 Response.Redirect ("~/notfound. aspx "); 
} 

... erfordert nur einen Anruf.

4
Keith Williams

Es hängt vom Kontext ab, aber ich werde im Allgemeinen null zurückgeben, wenn ich nach einem bestimmten Objekt suche (wie in Ihrem Beispiel), und eine leere Sammlung zurückgeben, wenn ich nach einer Reihe von Objekten suche, aber es gibt keine.

Wenn Sie in Ihrem Code einen Fehler gemacht haben und null zurückgibt, führt dies zu Nullzeigerausnahmen. Je früher Sie dies feststellen, desto besser. Wenn Sie ein leeres Objekt zurückgeben, funktioniert die erstmalige Verwendung möglicherweise, es können jedoch später Fehler auftreten.

3
Jacob Mattison

Am besten in diesem Fall "null" zurückgeben, in einem Fall gibt es keinen solchen Benutzer. Machen Sie auch Ihre Methode statisch.

Bearbeiten:

Normalerweise sind Methoden wie diese Mitglieder einer "User" -Klasse und haben keinen Zugriff auf ihre Instanzmitglieder. In diesem Fall sollte die Methode statisch sein, andernfalls müssen Sie eine Instanz von "User" erstellen und dann die Methode GetUserById aufrufen, die eine andere Instanz von "User" zurückgibt. Stimmen Sie zu, dass dies verwirrend ist. Wenn die GetUserById-Methode Mitglied einer "DatabaseFactory" -Klasse ist, ist es kein Problem, sie als Instanzmitglied zu belassen.

3
Kamarey

Ich bin ein französischer IT-Student, entschuldigen Sie mein schlechtes Englisch. In unseren Klassen wird uns gesagt, dass eine solche Methode niemals null oder ein leeres Objekt zurückgeben sollte. Der Benutzer dieser Methode sollte zunächst überprüfen, ob das gesuchte Objekt vorhanden ist, bevor er versucht, es abzurufen.

Unter Verwendung von Java werden wir gebeten, am Anfang jeder Methode, die null zurückgeben könnte, ein assert exists(object) : "You shouldn't try to access an object that doesn't exist"; einzufügen, um die "Vorbedingung" auszudrücken (ich weiß nicht, was das Wort auf Englisch ist).

IMO ist das wirklich nicht einfach zu benutzen, aber genau das warte ich auf etwas Besseres.

3
Saend

Wenn der Fall, dass der Benutzer nicht gefunden wird, häufig genug auftritt und Sie dies in Abhängigkeit von den Umständen auf verschiedene Weise tun möchten (manchmal eine Ausnahme auslösen, manchmal einen leeren Benutzer ersetzen), können Sie auch etwas verwenden, das nahe an F # 's Option oder Haskells Maybe -Typ, der explizit den 'no value'-Fall von' found something! 'trennt. Der Datenbankzugriffscode könnte folgendermaßen aussehen:

public Option<UserEntity> GetUserById(Guid userId)
{
 //Imagine some code here to access database.....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return Option<UserEntity>.Nothing; 
 else
    return Option.Just(existingUserEntity);
}

Und so verwendet werden:

Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
    // deal with it
} else {
    UserEntity value = result.GetValue();
}

Unglücklicherweise scheint jeder einen Typ wie diesen zu würfeln.

3
yatima2975

In unseren Business Objects gibt es zwei Haupt-Get-Methoden:

Um die Dinge im Kontext einfach zu halten, oder wenn Sie sie in Frage stellen, wären sie:

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

Die erste Methode wird beim Abrufen bestimmter Entitäten verwendet, die zweite Methode wird speziell beim Hinzufügen oder Bearbeiten von Entitäten auf Webseiten verwendet.

Dies ermöglicht es uns, das Beste aus beiden Welten in dem Kontext zu haben, in dem sie verwendet werden.

3
Mark Redman

Ich persönlich gebe eine Standardinstanz des Objekts zurück. Der Grund dafür ist, dass ich erwarte, dass die Methode null zu viele oder null zu eins zurückgibt (je nach Zweck der Methode). Der einzige Grund dafür, dass es sich bei diesem Ansatz um einen Fehlerzustand jeglicher Art handelt, besteht darin, dass die Methode keine Objekte zurückgibt und dies immer erwartet wurde (im Sinne einer Eins-zu-Viele- oder einer singulären Rückkehr).

Was die Annahme betrifft, dass es sich um eine Geschäftsdomänenfrage handelt, sehe ich das von dieser Seite der Gleichung aus einfach nicht. Die Normalisierung von Rückgabetypen ist eine gültige Frage der Anwendungsarchitektur. Zumindest ist es Gegenstand einer Standardisierung in der Codierungspraxis. Ich bezweifle, dass es einen Geschäftsbenutzer gibt, der sagen wird: "Geben Sie in Szenario X einfach eine Null".

3
Joseph Ferris

Normalerweise gebe ich null zurück. Es bietet einen schnellen und einfachen Mechanismus, mit dem festgestellt werden kann, ob etwas durcheinander ist, ohne Ausnahmen zu machen, und mit Tonnen von Versuchen/Fangen überall.

2
whatsisname

Interessante Frage und ich denke, es gibt keine "richtige" Antwort, da es immer auf die Verantwortung Ihres Codes ankommt. Weiß Ihre Methode, ob keine gefundenen Daten ein Problem darstellen oder nicht? In den meisten Fällen lautet die Antwort "Nein". Deshalb ist es perfekt, wenn der Anrufer null zurückgibt und die Situation behandelt.

Möglicherweise besteht ein guter Ansatz zur Unterscheidung von Auslösemethoden von Nullrückgabemethoden darin, eine Konvention in Ihrem Team zu finden: Methoden, die besagen, dass sie etwas "bekommen", sollten eine Ausnahme auslösen, wenn nichts zu bekommen ist. Methoden, die möglicherweise null zurückgeben, könnten anders benannt werden, möglicherweise stattdessen "Find ...".

2
Marc Wittke

Für Auflistungstypen würde ich eine leere Auflistung zurückgeben, für alle anderen Typen bevorzuge ich die Verwendung der NullObject-Muster zum Zurückgeben eines Objekts, das die gleiche Schnittstelle wie die des zurückgegebenen Typs implementiert. Einzelheiten zum Muster finden Sie unter Linktext

Unter Verwendung des NullObject-Musters wäre dies:

public UserEntity GetUserById(Guid userId)

{// Stellen Sie sich hier Code vor, um auf die Datenbank zuzugreifen .....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();  
 else
    return existingUserEntity;

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 
2
vikram nayak

Vergib mir meinen Pseudo-PHP/Code.

Ich denke, es hängt wirklich von der beabsichtigten Verwendung des Ergebnisses ab.

Wenn Sie den Rückgabewert bearbeiten/ändern und speichern möchten, geben Sie ein leeres Objekt zurück. Auf diese Weise können Sie mit derselben Funktion Daten in ein neues oder vorhandenes Objekt einfügen.

Angenommen, ich habe eine Funktion, die einen Primärschlüssel und ein Array von Daten annimmt, die Zeile mit Daten füllt und den resultierenden Datensatz in der Datenbank speichert. Da ich beabsichtige, das Objekt so oder so mit meinen Daten zu füllen, kann es von großem Vorteil sein, ein leeres Objekt vom Getter zurückzubekommen. Auf diese Weise kann ich in beiden Fällen identische Vorgänge ausführen. Sie verwenden das Ergebnis der Getter-Funktion, egal was passiert.

Beispiel:

function saveTheRow($prim_key, $data) {
    $row = getRowByPrimKey($prim_key);

    // Populate the data here

    $row->save();
}

Hier können wir sehen, dass dieselbe Reihe von Operationen alle Datensätze dieses Typs manipuliert.

Wenn jedoch die endgültige Absicht des Rückgabewerts darin besteht, etwas mit den Daten zu lesen und zu tun, würde ich null zurückgeben. Auf diese Weise kann ich sehr schnell feststellen, ob keine Daten zurückgegeben wurden, und dem Benutzer die entsprechende Meldung anzeigen.

Normalerweise fange ich in meiner Funktion Ausnahmen ab, die die Daten abrufen (damit ich Fehlermeldungen usw. protokollieren kann), und gebe dann null direkt vom Fang zurück. Für den Endbenutzer ist es im Allgemeinen unerheblich, was das Problem ist. Daher ist es am besten, meine Fehlerprotokollierung/-verarbeitung direkt in der Funktion zu kapseln, die die Daten abruft. Wenn Sie in einem großen Unternehmen eine gemeinsam genutzte Codebasis haben, ist dies besonders vorteilhaft, da Sie selbst dem faulsten Programmierer eine ordnungsgemäße Fehlerprotokollierung und -behandlung aufzwingen können.

Beispiel:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Das ist meine allgemeine Regel. Bisher hat es gut funktioniert.

2
user197794

Um es auf eine markigere Art und Weise auszudrücken ...

Ausnahmen gelten für außergewöhnliche Umstände

Wenn es sich bei dieser Methode um eine reine Datenzugriffsschicht handelt, würde ich sagen, dass bei einem bestimmten Parameter, der in einer select-Anweisung enthalten ist, möglicherweise keine Zeilen gefunden werden, aus denen ein Objekt erstellt werden kann. Daher ist die Rückgabe von null in diesem Fall akzeptabel ist Datenzugriffslogik.

Wenn ich andererseits erwartet hätte, dass mein Parameter einen Primärschlüssel widerspiegelt und ich nur die Zeile one zurückbekomme, würde ich eine Ausnahme auslösen, wenn ich mehr als eine zurückbekomme. 0 ist in Ordnung, null zurückzugeben, 2 nicht.

Wenn ich einen Anmeldecode hätte, der mit einem LDAP-Anbieter abgeglichen wurde, und dann mit einer Datenbank abgeglichen würde, um weitere Details zu erhalten, und erwartet hätte, dass diese jederzeit synchron sind, könnte ich die Ausnahme dann auslösen. Wie andere sagten, sind es Geschäftsregeln.

Jetzt werde ich sagen, dass dies eine allgemeine Regel ist. Es gibt Zeiten, in denen Sie das unterbrechen möchten. Meine Erfahrungen und Experimente mit C # (viel davon) und Java (ein bisschen davon) haben mich jedoch gelehrt, dass es viel teurer in Bezug auf die Leistung ist mit Ausnahmen umzugehen, als mit vorhersehbaren Problemen über bedingte Logik umzugehen. Ich spreche von 2 oder 3 Größenordnungen, die in einigen Fällen teurer sind. Wenn es also möglich ist, dass Ihr Code in einer Schleife endet, empfehle ich, null zurückzugeben und darauf zu testen.

2
Jim L

Wenn das zurückgegebene Objekt iteriert werden kann, würde ich ein leeres Objekt zurückgeben, damit ich nicht erst auf null testen muss.

Beispiel:

bool IsAdministrator(User user)
{
    var groupsOfUser = GetGroupsOfUser(user);

    // This foreach would cause a run time exception if groupsOfUser is null.
    foreach (var groupOfUser in groupsOfUser) 
    {
        if (groupOfUser.Name == "Administrators")
        {
            return true;
        }
    }

    return false;
}
2
Jan Aagaard

Ich möchte von keiner Methode null zurückgeben, sondern stattdessen den Funktionstyp Option verwenden. Methoden, die kein Ergebnis zurückgeben können, geben eine leere Option anstelle von null zurück.

Außerdem sollten solche Methoden, die kein Ergebnis zurückgeben können, dies durch ihren Namen anzeigen. Normalerweise setze ich Try oder TryGet oder TryFind am Anfang des Methodennamens, um anzuzeigen, dass möglicherweise ein leeres Ergebnis zurückgegeben wird (z. B. TryFindCustomer, TryLoadFile usw.).

Auf diese Weise kann der Aufrufer verschiedene Techniken anwenden, z. B. das Sammlungs-Pipelining (siehe Martin Fowlers Sammlungs-Pipeline ) für das Ergebnis.

Hier ist ein weiteres Beispiel, in dem die Rückgabe von Option anstelle von null verwendet wird, um die Codekomplexität zu verringern: So verringern Sie die zyklomatische Komplexität: Funktionstyp der Option

2
Zoran Horvat

Ich denke, Funktionen sollten nicht null zurückgeben, für die Gesundheit Ihrer Code-Basis. Ich kann mir ein paar Gründe vorstellen:

Es wird eine große Anzahl von Guard-Klauseln geben, die den Null-Verweis if (f() != null) behandeln.

Was ist null, ist es eine akzeptierte Antwort oder ein Problem? Ist null ein gültiger Status für ein bestimmtes Objekt? (Stellen Sie sich vor, Sie sind ein Client für den Code). Ich meine, alle Referenztypen können null sein, aber sollten sie?

Wenn null herumhängt, gibt es von Zeit zu Zeit ein paar unerwartete NullRef-Ausnahmen, wenn Ihre Codebasis wächst.

Es gibt einige Lösungen, tester-doer pattern oder Implementierung des option type aus der funktionalen Programmierung.

1
gavri

Mehr Fleisch zum Mahlen: Nehmen wir an, mein DAL gibt einen NULL-Wert für GetPersonByID zurück, wie von einigen empfohlen. Was soll meine (eher dünne) BLL tun, wenn sie eine NULL erhält? Geben Sie diese NULL weiter und lassen Sie den Endverbraucher sich darüber Sorgen machen (in diesem Fall eine ASP.Net-Seite)? Wie wäre es, wenn die BLL eine Ausnahme auslöst?

Die BLL wird möglicherweise von ASP.Net und Win App oder einer anderen Klassenbibliothek verwendet. Ich denke, es ist unfair zu erwarten, dass der Endverbraucher von sich aus "weiß", dass die Methode GetPersonByID eine Null zurückgibt (es sei denn, es werden Nulltypen verwendet, denke ich ).

Mein Ansatz (für das, was es wert ist) ist, dass mein DAL NULL zurückgibt, wenn nichts gefunden wird. FÜR EINIGE OBJEKTE ist das in Ordnung - es könnte eine 0: viele Liste von Dingen sein, also ist es in Ordnung, keine Dinge zu haben (z. B. eine Liste von Lieblingsbüchern). In diesem Fall gibt meine BLL eine leere Liste zurück. Bei den meisten Einzelobjekten (z. B. Benutzer, Konto, Rechnung) ist dies definitiv ein Problem und eine kostspielige Ausnahme. Da das Abrufen eines Benutzers über eine eindeutige Kennung, die zuvor von der Anwendung vergeben wurde, jedoch immer einen Benutzer zurückgeben sollte, handelt es sich bei der Ausnahme um eine "richtige" Ausnahme, wie bei der Ausnahme. Der Endverbraucher der BLL (ASP.Net, f'rinstance) erwartet immer nur, dass die Dinge unkompliziert sind. Daher wird ein Unhandled Exception Handler verwendet, anstatt jeden einzelnen Aufruf von GetPersonByID in einen try-catch-Block zu packen.

Wenn mein Ansatz ein offensichtliches Problem darstellt, lassen Sie es mich bitte wissen, da ich immer daran interessiert bin, etwas zu lernen. Wie andere Plakate bereits sagten, sind Ausnahmen kostspielig, und der Ansatz, zuerst zu prüfen, ist gut, aber Ausnahmen sollten genau das sein - außergewöhnlich.

Ich genieße diesen Beitrag, viele gute Vorschläge für "es hängt davon ab" Szenarien :-)

1
Mike Kingscott

Ich stimme den meisten Posts hier zu, die in Richtung null tendieren.

Mein Argument ist, dass das Generieren eines leeren Objekts mit nicht nullwertfähigen Eigenschaften Fehler verursachen kann. Eine Entität mit einer int ID - Eigenschaft hätte beispielsweise den Anfangswert ID = 0. Dies ist ein vollständig gültiger Wert. Sollte dieses Objekt unter bestimmten Umständen in einer Datenbank gespeichert werden, wäre dies eine schlechte Sache.

Für alles mit einem Iterator würde ich immer die leere Sammlung verwenden. So etwas wie

foreach (var eachValue in collection ?? new List<Type>(0))

ist meiner meinung nach codegeruch. Sammlungseigenschaften sollten niemals null sein.

Ein Edge-Fall ist String. Viele Leute sagen, dass String.IsNullOrEmpty Nicht wirklich notwendig ist, aber man kann nicht immer zwischen einer leeren Zeichenkette und null unterscheiden. Außerdem unterscheiden einige Datenbanksysteme (Oracle) überhaupt nicht zwischen ihnen ('' Wird als DBNULL gespeichert), sodass Sie gezwungen sind, sie gleich zu behandeln. Der Grund dafür ist, dass die meisten Zeichenfolgenwerte entweder von Benutzereingaben oder von externen Systemen stammen, während weder Textfelder noch die meisten Austauschformate unterschiedliche Darstellungen für '' Und null aufweisen. Selbst wenn der Benutzer einen Wert entfernen möchte, kann er nichts weiter tun, als die Eingabesteuerung zu löschen. Auch die Unterscheidung zwischen nullbaren und nicht nullbaren nvarchar Datenbankfeldern ist mehr als fraglich, wenn Ihr DBMS nicht Oracle ist - ein Pflichtfeld, das '' Zulässt, ist seltsam, Ihre Benutzeroberfläche würde dies niemals zulassen. Ihre Einschränkungen werden also nicht zugeordnet. Meiner Meinung nach lautet die Antwort hier, sie immer gleich zu behandeln.

Zu Ihrer Frage zu Ausnahmen und Leistung: Wenn Sie eine Ausnahme auslösen, die Sie nicht vollständig in Ihrer Programmlogik verarbeiten können, müssen Sie irgendwann das Programm abbrechen und den Benutzer auffordern, die gerade ausgeführten Aktionen zu wiederholen. In diesem Fall ist die Performance-Beeinträchtigung eines catch die geringste Ihrer Sorgen. Wenn Sie den Benutzer fragen müssen, befindet sich der Elefant im Raum Internet). Also, wenn Sie dem Anti-Muster von " Programmablauf mit Ausnahmen " nicht folgen, stören Sie sich nicht, werfen Sie einfach eines, wenn es Sinn macht. Selbst in Grenzfällen wie "Validation Exception" ist die Leistung kein Problem, da Sie den Benutzer auf jeden Fall erneut fragen müssen.

0
Oliver Schimmer

Ich bin verblüfft über die Anzahl der Antworten (im gesamten Web), die besagen, dass Sie zwei Methoden benötigen: eine "IsItThere ()" - und eine "GetItForMe ()" - Methode. Dies führt zu einer Race Condition. Was ist falsch an einer Funktion, die Null zurückgibt, diese einer Variablen zuweist und die Variable in einem Test auf Null überprüft? Mein früherer C-Code war übersät mit

if (NULL! = (Variable = Funktion (Argumente ...))) {

Sie erhalten also den Wert (oder null) in einer Variablen und das Ergebnis auf einmal. Wurde diese Redewendung vergessen? Warum?

0
no comprende

Ein asynchrones TryGet-Muster:

Für synchrone Methoden glaube ich, dass @ Johann GerellsAntwort das in allen Fällen zu verwenden ist.

Das TryGet-Muster mit dem Parameter out funktioniert jedoch nicht mit Async-Methoden.

Mit den Tuple-Literalen von C # 7 können Sie dies jetzt tun:

async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
    if (InternalIdExists(id))
    {
        o = await InternalGetSomeObjectAsync(id);

        return (true, o);
    }
    else
    {
        return (false, default(SomeObject));
    }
}
0
ttugates