Am Wochenende konnte ich endlich wieder ein wenig an meinem privaten Projekt arbeiten und bin voll in eine Anfängerfalle gestolpert. Ich arbeite an einer Art Lottozahlengenerator und habe (natürlich) die Lottozahlen in eine eigene Klasse gepackt um Wertebereichsüberprüfung (Lottozahlen können nur zwischen 0 und 50 liegen) o.ä. direkt im Model abzufrühstücken (SoC) wobei die dahinter liegende Architektur natürlich etwas komplexer ist.

Nun bringt es eine solche kleinteilige Datenhaltungsklasse mit sich, dass verschiedene Basisfunktionalitäten erneut auszuprogrammieren sind. Wie eben die Vergleichsoperatoren, nur hatte ich dort nicht an das böse Null und die Rekursionshexe gedacht.


Meine erste Lösung sah wie folgt aus:

public static bool operator ==(LottoBasicNumber left, LottoBasicNumber right)
{
   if (left == null && right == null)
      return true;
   if (left == null || right == null)
      return false;
   return left.Value == right.Value;
}

An sich sieht das nicht verkehrt aus, bedenkt man aber die Arbeitsweise des Operators stolpert man recht schnell über die eigenen Beine. Denn wenn der linke Operator gegen den rechten geprüft wird, was passiert dann wohl bei left == null? Richtig, die Funktion wird noch einmal aufgerufen. Dabei bleibt left gleich und right wird auf null gesetzt. Im neuen Durchlauf wird erneut left == null geprüft und der Spaß beginnt von vorn, solange bis der Stack voll ist und eine StackOverflowException fliegt.

Was also tun? Das if entfernen? Ganz sicher nicht, denn dann fliegt eine NullReferenceException sobald man eine Instanz der Klasse gegen null prüft. Man müsste den Vergleich also auf eine andere Ebene anheben damit nicht mehr unser Vergleichsoperator aufgerufen wird. Genau das ist auch die Lösung. Wir casten die eingehenden Objekte einfach auf object damit wird der Standardvergleichsoperator aufgerufen und die Rekursion ist passé.

public static bool operator ==(LottoBasicNumber left, LottoBasicNumber right)
{
   if ((left as object) == null && (right as object) == null)
      return true;
   if ((left as object) == null || (right as object) == null)
      return false;
   return left.Value == right.Value;
}

An sich ganz leicht. Da ich aber an dieser Stelle aus Faulheit auf TDD verzichtet hatte und 16 von 18 Tests der darüber liegenden Schicht scheinbar ohne erfindlichen Grund schief gingen, ist es für mich mal wieder ein Zeichen nicht aus auf Unit-Tests zu verzichten, selbst wenn die Funktion noch so unkompliziert wirkt.