1. Das Problem NullReferenceException
Fast jeder Anfänger (und nicht nur die!) in C# ist mindestens einmal über diese gruselige Meldung gestolpert:
System.NullReferenceException: Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
Hier ein echter Klassiker:
string hallo = null;
Console.WriteLine(hallo.Length); // BUMM! NullReferenceException
Die Idee ist simpel: Du versuchst, auf ein Objekt zuzugreifen, das gar nicht existiert. Die Variable zeigt auf null – also auf ein leeres Nichts statt auf ein Objekt.
Warum passiert der Fehler so leicht?
Weil die meisten Referenztypen in C# historisch gesehen null sein konnten. Entwickler haben oft vergessen zu prüfen, ob die Variable wirklich auf ein Objekt zeigt – und zack, schon hast du die berühmte NullReferenceException gefangen.
Wenn ein Entwickler für jede NullReferenceException einen Dollar bekommen würde, hätte er längst sein eigenes Betriebssystem geschrieben.
Warum ist das ein Problem?
Früher haben wir gelernt, dass Werte wie int? oder double? null sein können – für Fälle, in denen wir explizit "kein Wert vorhanden" markieren wollen (zum Beispiel, wenn ein Feld in der Datenbank leer ist).
Aber Referenztypen (wie string, beliebige Klassen, Arrays usw.) konnten schon immer null sein. Das war von Anfang an so in C#. Praktisch, aber auch gefährlich – weil die Sprache uns nicht gezwungen hat, explizit zu überlegen: "Kann dieser Verweis vielleicht leer sein?"
2. Evolution: Nullable Reference Types (NRT)
Vor 5 Jahren kam ein Feature, das den Umgang mit NullReferenceException komplett auf den Kopf gestellt hat – Nullable Reference Types (NRT).
string s = "hallo"; // not-nullable reference
string? vielleicht = null; // nullable reference
Die Hauptidee:
- Strikte Trennung zwischen Referenzvariablen, die auf keinen Fall null sein dürfen, und solchen, die leer sein dürfen.
- Dem Entwickler schon beim Kompilieren zeigen, wo es potenziell Probleme mit null geben könnte – nicht erst zur Laufzeit (wenn es zu spät ist).
In neuen C#-Versionen hat sich die Art, wie Referenzvariablen deklariert werden, geändert: Standardmäßig darfst du kein null zuweisen, außer du erlaubst es explizit.
So einfach – nur ein ? und die Bedeutung der Variable ist komplett anders!
3. Nullable Reference Types aktivieren
Standardmäßig ist diese strikte Syntax deaktiviert in den meisten Projekten, damit alter Code nicht kaputtgeht. Aber moderne Projektvorlagen in Visual Studio, Rider und .NET CLI erstellen Projekte schon mit aktiviertem NRT – oder empfehlen es dir zumindest dringend.
Um sicher zu gehen, ob NRT in deinem Projekt aktiviert ist, such im .csproj-File nach dieser Zeile:
<Nullable>enable</Nullable>
Wenn die Zeile fehlt, kannst du sie einfach hinzufügen – das ist sicher.
Wie wirkt sich das auf den Code aus?
- Wenn NRT deaktiviert ist (altes Verhalten): string s = null; – kein Fehler, alles egal.
- Wenn aktiviert: Der Compiler meckert, wenn du versuchst, null in etwas zu stecken, das nicht null sein darf.
4. Beispiele mit NRT
Einfaches Beispiel
#nullable enable // Diese Zeile aktiviert NRT-Checks für diese Datei
string nichtNullable = "Hallo";
string nichtNullable2 = null; // KOMPILIERFEHLER!
string? nullableString = null; // Alles ok, wir haben null explizit erlaubt
In der ersten Zeile deklarieren wir einen String, der immer auf ein echtes Objekt zeigen muss.
In der dritten Zeile deklarieren wir einen String, der null sein darf.
Überprüfung auf null
void PrintLength(string? s)
{
// Der Compiler meckert: "Was, wenn s == null?"
Console.WriteLine(s.Length);
// So ist's gut
if (s != null)
{
Console.WriteLine(s.Length);
}
}
Der Compiler hilft dir jetzt, die Checks nicht zu vergessen!
Compiler-Warnungen
Wenn du die Warnung ignorierst und trotzdem auf eine nullable-Variable ohne Check zugreifst – bekommst du eine neue (und sehr nützliche!) Compiler-Warnung. Das ist kein Fehler (das Programm baut trotzdem), aber das gelbe "Lämpchen" sagt: "Bist du sicher, Kumpel?"
Vergleich der Modi:
| C#-Typ | Kann null sein? | Legacy-Modus (vor NRT) | NRT-Modus (#nullable enable) |
|---|---|---|---|
| int | Nein | Nein | Nein |
| int? | Ja | Ja | Ja |
| string | Ja | Nein (immer erlaubt) | Nein (standardmäßig nicht erlaubt) |
| string? | Ja | Nein | Ja |
5. Tipps: Wie und warum NRT nutzen?
- Weniger Bugs: Weniger unerwartete Abstürze wegen null, mehr Freude für Entwickler und User.
- Klarerer Code: Du siehst sofort, was leer sein darf und was immer gesetzt sein muss.
- Hilfe vom Compiler: Gib's zu – er gibt sich echt Mühe für uns! NRT-Warnungen sind eine wertvolle Infoquelle für potenzielle Fehler.
Wo ist das unverzichtbar?
- In großen Projekten, wo viele Leute am selben Code arbeiten.
- In APIs und öffentlichen Bibliotheken – damit andere wissen, was sie dürfen und was nicht.
- Überall, wo Zuverlässigkeit wichtig ist (z.B. Banking-Apps, medizinische Systeme usw.)
6. Typische Fehler und Stolperfallen
- "Das ? vergessen"
Du weist null einer normalen Zeichenkette zu (string s = null;) – und der Compiler meckert, weil eine normale Zeichenkette jetzt standardmäßig nicht null sein darf. - "Zu viel ?"
Du machst einfach alle Variablen zu string?, nur damit der Compiler nicht meckert. Der Sinn ist aber, gezielt zu markieren, wo ein leerer Wert wirklich erlaubt ist. - "Warnung falsch verstanden"
Du ignorierst die Warnung und fängst dir dann doch eine NullReferenceException ein, wo du eigentlich auf den Compiler vertraut hast.
GO TO FULL VERSION