C# Basics Teil 4

Hier der vierte Teil zu den Basics…

Nullbare Typen

Es ist allgemein bekannt, dass man Value-Typen nicht auf null prüfen oder damit initialisieren kann, da es sich ja nicht um Referenzen handelt. Nun, das ist grundsätzlich immer noch richtig. Doch C# hat ab 2.0 ein neues Feature eingebaut, damit man jede Art von Value-Typen “nullen” kann :-). Der Gebraucht ist sehr simpel. Jeder Typ kann mit einem Postfix in Form eines “?” zu einem nullbaren Typen gemacht werden.

Beispiel:

int a = null; // Kompiler-Error
int? b = null; // OK
if (b == null) { b = 1; }
if (b > 1) { b = 2; }

Warum funktioniert das?

Die Lösung des Rätzels ist einfach. Der C# Kompiler bedient sich einem neuen generischen Typen Nullable<T>. Bei der Kompilation wird der int? einfach in einen Nullable<int> umgewandelt. Ein int? ist so gesehen also kein wirklicher int mehr, sondern eine Instanz von Nullable, die den int hostet.

Man könnte sich nun fragen, warum dann ein Vergleich wie b > 1 noch funktioniert. Wenn der int? ja zu Nullable<int> transformiert wurde, dürfte ein Vergleich mit einem int nicht mehr funktionieren. Zudem Implementiert Nullable<> keine Operatoren <, > oder ==. Auch hier hat der Kompiler seine Hand im Spiel. Man nennt das ganze “Lifted Operators”. Der Kompiler klaut (oder hebt) den jeweiligen Operator vom darunterliegenden Typen an und schreibt einen neuen Ausdruck.

Beispiel:

int? a = 10; // OK
bool b = a > 5;

wird vom Kompiler zu folgendem Ausdruck übersetzt:

Nullable<int> a = new Nullable<int>(10);
bool b = a.HasValue ? a.Value > 5 : false;

Hinweis:

Vorsicht beim Vergleichen von Typen.

Nullable<int> a = 10;   // oder int?
bool isInt = a.GetType().Equals(typeof(int));   // true
bool isNullable = a.GetType().Equals(typeof(Nullable<int>));  // false!!!

Man sieht, obwohl die Variable a als Nullable<int> deklariert wurde, wird ein typeof(Nullable<int>) nicht funktionieren. Warum: a ist eine Instanz von Nullable<int>.  Der Kompiler “liftet” in diesem Falle die Operatoren mit denjenigen vom darunterliegenen Typen… und so auch den typeof-Operator!

Aber Vorsicht, es handelt sich dabei nie um einen wirklichen int. Prüft man die Instanz z.B. auf die Implementation eines IConvertible-Interface, wird dieser Test mit false quittiert, weil Nullable zwar die Operatoren liftet, jedoch die Schnittstellen der darunterliegenden Klasse in Ruhe lässt. Obwohl es bei der Konvertierung eigentlich keine Probleme geben sollte, gibt es im Framework eine spezielle Klasse dafür mit dem Namen NullableConverter.

Das Schlüsselwort fixed

Den meisten wird das Schlüsselwort fixed noch nie begegnet sein. Warum ist einfach zu erklären. Hierbei handelt es sich um eine Spezialität im Bereich von unsicherem Code.

Wozu dient es: Der GC (garbage collector) schiebt aus Effizienz- und Platzgründen während der Programmausführung die Objekte im Speicher hin und her und das Memory fragmentiert. Dies kann in gewissen Fällen Proleme verursachen, wenn man in einem mit unsafe markierten Codestück gerade dabei ist über die Memory-Adressen zu loopen. Mit dem fixed-Statement teilt man dem GC mit, dass er die Objekte darin in Ruhe lassen soll. Dies steigert zudem die Effizienz beim Durchlauf.

Beispiel:

unsafe
{
string myString = “Hallo”;
fixed (char* p = myString) { *p = ‘A’; }
}

Das Objekt myString wird vom GC innerhalb des fixed-Blockes in Ruhe gelassen. Man beachte zudem die Zuweisung der Adresse vom myString. Diese Zuweisung braucht keinen Adress-Operator “&”, da es sich beim String-Objekt ja bereits um eine Referenz handelt.