1. Einführung
Stell dir mal folgende Situation vor. Du speicherst Daten über Katzen im Code. Und du willst neben dem Alter der Katze zum Beispiel auch Zustände wie "keine Daten" oder "Alter unbekannt" speichern. Man könnte 0 speichern, aber Null ist ja ein ganz legitimes Alter.
int age = 0; // Alter der Katze. Aber was ist 0? Ein Kätzchen oder "unbekannt"?
Das Problem ist, dass int nur ganze Zahlen annimmt, aber "keine Daten" ist bei Zahlen nicht vorgesehen. Wenn das Alter ein String wäre, könnte man null zuweisen. Aber mit Zahlen klappt das nicht:
int age = null; // Fehler! Du kannst null nicht einer int-Variable zuweisen
Das liegt daran, dass Value Types (structs wie int, double, DateTime, bool) immer irgendwas speichern. Die haben keinen "leeren" Zustand (anders als Reference Types, wo null einfach heißt, dass kein Objekt da ist).
Beispiel aus dem Alltag:
Um das zu umgehen, denken sich Programmierer manchmal "besondere" Werte aus: zum Beispiel -1 oder int.MaxValue für "kein Wert". Aber das ist hässlich, unpraktisch und gefährlich: Man kann echte Werte leicht mit Platzhaltern verwechseln.
2. Nullable-Typ: Ein Typ, der auch "null" kann
Die Idee
Was wäre, wenn man normalen Zahlen (und generell allen Value Types) erlauben würde, nicht nur einen Wert, sondern auch null zu speichern?
In C# geht das mit einer speziellen Wrapper-Klasse Nullable<T>. So eine Klasse kann entweder einen Wert vom Typ T enthalten oder nichts (also null).
Syntax
int? age = null; // Die Variable age kann eine Zahl oder null sein
Der C#-Compiler versteht so einen Code eigentlich so:
Nullable<int> age = new Nullable<int>(); // Die Variable age hat keinen Wert
Oder, wenn du explizit einen Wert angeben willst:
Nullable<int> age = new Nullable<int>(42); // age enthält die Zahl 42
Das Fragezeichen macht also aus jedem Value Type seine Nullable-Version:
- int? (das gleiche wie Nullable<int>)
- double?
- DateTime?
- und alle anderen Structs
Nullable-Wrapper für die beliebtesten Value Types
| Normaler Typ | Nullable-Typ | Beispiel |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Du kannst das auch länger schreiben, mit dem Wrapper-Klassen-Syntax:
Nullable<int> pages = null;
Aber int? zu schreiben ist kürzer und damit cooler.
3. Wie benutzt man Nullable-Typen?
Zuweisung und Überprüfung
Das Zuweisen von null funktioniert genauso wie bei Reference Types:
Deklaration und Zuweisung
int? temperature = null;
temperature = 25;
Check auf null
Wie findest du raus, ob dein Nullable-Type einen echten Wert hat?
if (temperature != null)
{
Console.WriteLine($"Temperatur: {temperature}");
}
else
{
Console.WriteLine("Keine Temperaturdaten");
}
Properties von Nullable-Typen: .HasValue und .Value
Nullable-Typen haben zwei wichtige Properties:
- .HasValue — gibt true zurück, wenn die Variable was enthält (also nicht null ist).
- .Value — der eigentliche Wert, falls vorhanden (sonst gibt's eine Exception).
int? temperature = null;
if (temperature.HasValue) //keine Exception!
{
Console.WriteLine($"Temperatur: {temperature.Value}°C");
}
else
{
Console.WriteLine("Temperatur unbekannt");
}
Dieser Code läuft: temperature ist nicht null, sondern enthält null intern. Du kannst immer die Property HasValue bei einem Nullable-Typ aufrufen. Aber .Value holen, wenn nix drin ist – schlechte Idee, dann gibt's eine InvalidOperationException. Also immer vorher checken!
Kurzschreibweise beim Ausgeben
Oft reicht es, die Nullable-Variable einfach zu benutzen – C# checkt das dann schon:
int? hours = null;
Console.WriteLine(hours); // gibt nix aus (einfach leer)
hours = 10;
Console.WriteLine(hours); // gibt 10 aus
4. Operationen und Nullable-Typen: Rechnen, Vergleiche, Konvertierungen
Operationen: Was passiert?
Wenn einer der Operanden nullable ist, ist das Ergebnis auch nullable.
Und wenn einer der Operanden null ist, ist das Ergebnis auch null.
int? a = 5;
int? b = null;
int? summe = a + b; // summe == null
int? c = 10;
int? d = 15;
int? gesamt = c + d; // gesamt == 25
Vergleichsoperationen
Du kannst Nullable-Typen mit normalen Zahlen vergleichen:
int? score = null;
if (score > 0) Console.WriteLine("Super Ergebnis!");
else Console.WriteLine("Kein Ergebnis"); // Dieser Zweig läuft
Wenn score null ist, ist die Bedingung score > 0 false.
Explizite und implizite Konvertierung
- Von normalem Typ zu nullable: automatisch.
- Von nullable zu normal: nur wenn nicht null (sonst Exception).
int? x = 3;
int y = x.Value; // Ok, wenn x nicht null ist
// Implizit
int? z = y;
5. Nullable und Methoden: Parameter und Rückgabewerte
Nullable-Werte zurückgeben
Wenn eine Methode nicht immer ein Ergebnis liefern kann, nimm Nullable-Typen als Rückgabewert:
// Die Methode sucht einen User nach Login und gibt sein Alter zurück, falls gefunden, sonst null
int? FindUserAge(string login)
{
// ... hier kommt die Suchlogik
return null; // falls nicht gefunden
}
Nullable-Parameter
Du kannst Nullable-Werte als Parameter nehmen, um klarzumachen: "kann sein, muss aber nicht".
void PrintTemperature(int? temp)
{
if (temp == null)
Console.WriteLine("Temperatur unbekannt");
else
Console.WriteLine($"Temperatur: {temp} Grad");
}
GO TO FULL VERSION