Eine angeregte Diskussion ist gestern “ent-Bandt” :-) mit den Blog-Posts über den kleinen ToListOrDefault()-Helper und die dadurch entstandende Null-Verständnis-Thematik sowie der philosophischen Ausführung über das Null oder nicht Null in Anwendungen. Man kann in den Posts wunderbar nachlesen, worum es geht – deswegen hier nur eine kleine Rekapitulation.

Thomas stellt zur Debatte, ob man aus Repository-Methoden ab und zu auch mal null zurückgeben darf/kann/soll. So z.B. bei so einer Methode:

User GetUserByName(string name);

Mache das Unmögliche möglich?!?

Einige Leute finden das garnicht besonders gut. Sie bevorzugen lieber soetwas wie einen “nicht-existenten” User zurückzugeben. Also eine Instanz einer Klasse, die eigentlich garnicht möglich wäre (weil sie ja die nicht exsistierende Instanz darstellt). Programmtechnisch ist das sicherlich recht einfach lösbar – oftmals mit einer statischen Property, die sich wiederum eines privaten Konstruktors bedient. In dem obigen Beispiel könnte eine derartige Implementierung ungefähr so aussehen:

public User GetUserByName(string name)
{
  if (!repository.UserExists(name))
    return User.NotExistent;

  //user laden und zurückgeben
}

Eine zweite, nicht minder bevorzugte Alternative zur Rückgabe von NULL ist die beliebte Exception. Falls also kein Benutzer unter dem angefragten Namen existieren sollte, dann soll eine Ausnahme signalisiert werden. Ganz einfach implementiert:

public User GetUserByName(string name)
{
  if (!repository.UserExists(name))
    throw new UserNotFoundException(name);

  // user laden und zurückgeben
}

Beides sind sicherlich mögliche und sogar gute Alternativen zu der Implementierungsvariante, die Thomas zur Diskussion gestellt hat:

public User GetUserByName(string name)
{
  if (!repository.UserExists(name))
    return null;

  // user laden und zurückgeben
}

Um es kurz zu fassen: Alle drei Varianten sind möglich, alle drei Varianten sind gut, alle drei Varianten sind schlecht. Es kommt auf den Anwendungsfall an. Die beliebte “It-Depends”-Weisheit streckt das Thema mit geballter Kraft nieder. Dennoch ist es eine Untersuchung wert, denn ungewöhnlicherweise wird die NULL-Variante kategorisch als falsch und “böse” bewertet.

Nullbivalenz

NULL ist ein besonderer Wert. Er ist der Wert, der eigentlich nicht zugewiesen werden kann. Genauer genommen ist er kein Wert, sondern nichts anderes als die knapp in vier Buchstaben formulierte Aussage “Ein für eine bestimmte Werteklasse belegter Ort wurde mit keinem Wert belegt”. Ergo: Null ist kein Wert. Ja, aber ist das denn nicht genau das, was GetUserByName ausdrücken und zurückgeben muss, wenn es keinen Benutzer für den gegebenen Namen findet?

Schaut man sich die erste Alternative zu NULL an (also das User.NonExistent-Konstrukt), dann kann man sagen: Joa, ok – ist aber das Gleiche. User.NonExistent ist sicherlich expliziter als NULL. NULL ist aber schon da und genau für solche Dinge gedacht. Die Aufgabe von NULL ist ja genau die, keinen Wert darzustellen.

Und wie sieht es mit Alternative Zwei – der UserNotFoundException aus? Die Excpetion ist auch explizit. Mehr noch, die Exception ist flexibel und rigoros gleichermaßen. Flexibel, weil sie jederzeit aus dem Nichts auftauchen kann. Rigoros, weil sie eine Fülle von Informationen über den Kontext sammeln und mitgeben kann. Die Exception sagt hier klar und deutlich: “Es gibt keinen Wert, weil der Benutzer {name} nicht gefunden wurde.”. Scheinbar ein Vorteil gegenüber den anderen Varianten. Im Ergebnis ist es jedoch nicht wesentlich von den anderen Herangehensweisen unterscheidbar.

Null Sicherheit

Nun, offensichtlich ist die sprechendere Variante der Exception vorteilhaft und damit dem NULL vorzuziehen. Doch beim zweiten Blick entpuppt sich der Vorteil als nicht so deutlich als zunächst vermutet. Denn in der Praxis kann sich die “unklare” und “unexplizite” Art von NULL wieder als erwünscht erweisen.

So muss man bei Exceptions, um den Mehrwert zu kennen, auch deren Typ genau kennen. Man muss also Wissen, das es sich um eine UserNotFoundException handelt. Erst dann kann man auch erfahren, welcher ominöse Benutzer unauffindbar ist. Das kann durchaus problematisch werden, wenn man also im höheren Callstack wissen muss, um welche Exception es geht, denn schliesslich schafft man sich dadurch eine Abhängigkeit auf den niedrigeren Code.

Null ist also eine unsichere, uninformative, aber immer verfügbare und unabhängige Variante, dem Aufrufer zu sagen: “Es gibt keinen Wert für das, was Du von mir verlangst”. Wenn man sich mit der Anwendung von NULL jahrelang auseinandergetzt hat und auch ein wenig die ungeliebten Interna von Speichermanagement & Datenstrukturen hineinblickt, dann wird NULL schnell zu einer praktikablen Lösung für schwierige Situationen.

Doch vor Allem bei NULL stellt man immer wieder fest: praktikabel und elegant sind zwei verschiedene paar Schuhe. Denn NULL ist genauso nichtssagend wie vielseitig. NULL zwingt den Aufrufer zum Abwägen: Soll ich das Ergebnis gleich auswerten oder lieber überprüfen? Ein wunderbares Beispiel dafür ist sicherlich der “if (x != null)”-Check – die Null-Prüfung. Das wird vor Allem dann zum Problem, wenn man mit (gewollt oder ungewollt) unbekannten Komponenten arbeitet. Im Interface oder in der Typsignatur steht es jedenfalls sehr selten erkennbar drin, ob nun in Einzelfällen NULL zurückgeliefert wird oder nicht.

Null ist nicht gleich Null

Für mich gibt es beim Umgang und bei der Anwendung von Null keine Faustregel. Ich gebe in einigen Methoden NULL zurück. Meistens genau dann, wenn ich wirklich damit ausdrücken möchte, dass etwas katastrophales passiert ist. Ich finde es sehr schwerwiegend, NULL als Zuweisung oder Rückgabe stehen zu lassen – aber ich mache es manchmal ganz bewusst. In einer Anwendung, in der ich z.B. Benutzer über einen Authentifizierungsdienst identifizieren muss, bevor ich überhaupt etwas anderes machen kann, kann die folgende Signatur NULL zurückliefern:

interface IAuthenticationService
{
  Account SignIn(string user, string password);
}

Es kann alles mögliche passiert sein – keine Verbindung zum Server, falscher Server, Verbindungsfehler, Connection Timouts, Benutzer nicht gefunden, Benutzer gesperrt, Passwort abgelaufen, falsches Passwort – all dies würde ich versuchen über Exceptions oder Return-Codes zu erledigen. Die meisten der Exceptions gibt es ja schon frei Haus vom Framework. Aber für das mich Unbekannte und Unerwartete gibt es immer noch eine Rückgabe, und die heisst NULL.

Null-Summen-Spiel

Generell kann ich für mich nur sagen, dass ich gelernt habe mit NULL sehr vorsichtig umzugehen. Ich vermeide es so gut wie möglich. Exceptions sind ein gutes Mittel – allerdings setze ich sie auch nicht sehr oft ein. Die Bürde der Abhängigkeit ist schon da – obwohl es natürlich in der Contract-Assembly definiert ist. Bei Repositories habe ich (fast) immer Ergebnisse die nicht NULL liefern. Andererseits setze ich NULL als Rückgabe auch öfter ein, wenn ich nur eine “schwere” Ausnahme in meinem Code feststelle.

Im obigen Beispiel hätte ich wohl NULL als Rückgabe toleriert, vor Allem, wenn ich davon ausgehen kann, dass es oft – oder sehr oft – dazu kommen kann und diese Tatsache ein schwerwiegendes Problem darstellt. Ein schönes Beispiel sind immer wieder die Brute-Force und DOS-Attacken auf Webportale.

Ich hätte wahrscheinlich nicht NULL zurückgegeben, sondern eine Exception ausgelöst, wenn ich das Ganze als wiederverwendbare, allgemeine Kompontente entwickeln würde.

So oder so – beides ist machbar und beides hat seine Berechtigung. Klar und deutlich soll aber abschließend erwähnt sein: NULL zu vermeiden ist eine gute Sache. Es macht den Code expliziter, offensichtlicher und lesbarer.

Comments
This article has 4 comments:

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • [...] Ilker Cetinkaya...
    by Jürgen Gutsch : Katastrophenfall ‘NULL’?
    on May 3rd 2010

    [...] Ilker Cetinkaya konnte es sich diesmal nicht verkneifen ebenfalls etwas zu dem Thema zu schreiben: Null Toleranz, vorauf hin Ralf schlagfertig reagierte: Es hilft nichts, dass es darauf ankommt – nicht nur beim [...]

  • Gelungener Beitrag! Deine...
    by Mike Bild
    on May 3rd 2010

    Gelungener Beitrag! Deine Erläuterungen zeigen sehr praktisch die NULL Probleme im Verständnis der Anwender von Funktionseinheiten. Ich bin der Meinung, dass eine solche, pragmatische und mögliche Umsetzung der Problemdomäne (Anmeldung eines Benutzers) mit NULL als “Status undefiniert” Hilfskonstruktion gut zum Szenario passt. Dennoch habe ich beim Umgang mit NULL als einen im Allgemeinen zulässigen Statuswert so meine Probleme. Sie macht den Code unleserlich. Ständige NULL-Prüfungen (Status-Fallunterscheidungen) im Code ala “if (x != null)-Check” sind nötig. Ständige Wiederholungen. Exceptions sind ebenso unschön als Statuswert-Hilfsmittel. Sie sind vielleicht sogar noch schlimmer. Ich meine, Status ist überhaupt aus Funktionen zu verbannen. Und was Status benötigt, wird besonders behandelt und separiert. Darunter eben Datenstrukturen und Funktionen die auf einmal mit NULL oder Exceptions umgehen müssen. Allgemeine Statuswerte finde ich in Anwendungen sehr problematisch. Ich muss immer prüfen und überlegen und Fallunterscheidungen formulieren. Ein unschöner, aufwändiger und fehleranfälliger Codeteil innerhalb von Funktionseinheiten. Zumal sie Funktionsverkettungen und ähnliches mit LINQ oder Fluent-Interfaces wegen NULL-Reference Exceptions erschweren. Überhaupt, NULLReferenceExceptions sind ein weiterer Grund warum ich den Einsatz von NULL als Parameter oder Rückgabe in heutigen Anwendungen nicht mehr zustimmen würde. (Brown-Field mal ausgenommen) Ich denke, heute können wir das, entsprechendes Anwendungsdesign bzw. Architektur bzw. Regeln, besser. Hier gibt bereits Pattern zur Umsetzung wie Identity- und Maybe-Monaden. (Container) Diese kapseln Zustände in Datenstrukturen und befreien (viele und nicht immer alle) Funktionen von Status, Exceptions und Fallunterscheidungen.

  • [...] einige haben...
    by .NET Stories: Digitale Erfahrungen » Blog Archive » Kein Yin ohne Yang, kein Null ohne Pointer
    on May 2nd 2010

    [...] einige haben auch Blog-Post-Antworten geschrieben. Ich habe das mit Kommentaren und dem Null Toleranz-Beitrag auch getan, der unermüdliche Ralf hat auch kräftig in die Tasten gehauen. Überraschenderweise [...]

  • Ich konnte nicht...
    by Ralf Westphal
    on May 2nd 2010

(c) 2000-2012 ilker.de - Creative Computing.

For any case of inquiry regarding this document, you can always contact the website owner.