Für Abstürze von .NET Programmen sind oft NullReferenceExceptions verantwortlich. Dies ist sogar vielfach die häufigste Ursache.
Aussagekraft von NullReferenceExceptions
Ohne Debugger ist leider die Ursache der NullReferenceException oft nicht klar. Dies auch, wenn Zeilennummern im Stack-Trace ersichtlich sind. Denn eine einzelne Zeile könnte oft an mehreren Orten eine NullReferenceException auslösen. Zudem ist die Zeilenangabe nicht immer zuverlässig.
Nachdem der C# as-Operator verwendet wurde, kann eine NullReferenceException auch auftreten, weil die Konvertierung nicht durchgeführt werden konnte.
Abhilfe
Deutlich minimieren lassen sich die hässlichen NullReferenceExceptions und die ArgumentNullExceptions durch die Aktivierung von “Nullable Reference Types”. Dadurch führt der C# Compiler diverse zusätzliche Prüfungen durch, welche Warnungen generieren können.
Aktivierung
Aktivieren lässt sich das Feature “Nullable Reference Types” durch Anpassung des Projekt Files csproj. Dazu wird <Nullable> auf “enable” gesetzt:
<PropertyGroup> <Nullable>enable</Nullable>
Die Aktivierung kann auch im GUI “Projekt Properties” erfolgen.
Es ist auch möglich, dass die Aktivierung nur für Teile des Projekts erfolgt. Dies geschieht im C# Source File mit der folgenden Direktive:
#nullable enable
Wenn ein Projekt auf eine neue .NET Version migriert wird, werden “Nullable Reference Types” nie automatisch aktiviert.
Falls “Nullable” nicht gesetzt wurde, wird “disable” verwendet. “Nullable” erlaubt auch die Werte “warnings” und “annotations”. Diese Werte erlauben eine teilweise Aktivierung und sind weniger relevant.
Warnungen
Mit der aktivierten Option für “Nullable Reference Types” sollen nun Referenzen, für welche null zulässig ist, bei der Deklaration um ein Fragezeichen ergänzt werden. Beispiel:
string? name;
Der Compiler generiert, abhängig von der Referenz Deklaration, Warnungen:
Referenz | Warnung falls |
---|---|
Traditionell (Deklaration ohne “?”) |
|
Nullable (Deklaration mit “?”) |
|
Das folgende Bild zeigt ein paar konkrete Warnungen:
Null Forgiving Operator
Das Ausrufezeichen dient als “Null Forgiving Operator”. Dieser kann eingesetzt werden, wenn man ganz sicher ist, dass eine Referenz nicht null ist. Dadurch wird eine Warnung unterdrückt. Beispiel:
name!.Length
Der Operator kann aber auch wie folgt verwendet werden, z.B. bei der Initialisierung eines Properties:
public Product Product { get; set; } = null!;
Mindestens auf den ersten Blick macht diese Verwendung keinen Sinn. Falls dieses Property aber mit einem Wert ungleich null initialisiert wird, dann kann diese Verwendung hilfreich sein. Dies kann zutreffen, wenn z.B. Entity Framework Core verwendet wird.
Falls C# 11 oder höher eingesetzt wird, sollte für diesen Fall die folgende, besser verständlichere Syntax verwendet werden:
public required Product Product { get; set; }
C# Versionen
Version | Informationen |
---|---|
Ab C# 8 (.NET Core 3 / .NET Standard 2.1) |
|
Ab C# 10 (.NET 6) |
|
Praxiserfahrungen
Wenn man beginnt, mit “Nullable Reference Types” zu arbeiten, ist es am Anfang teilweise etwas mühsam, weil man mehr Warnungen erhält. Für neu erstellte C# Projekte lohnt es sich aber auf jeden Fall. Und auch für Projekte, die noch nicht released sind.
Wenn ein bestehendes Projekt umgestellt wird, kann dies zu vielen Warnungen führen. Spätestens wenn ein Projekt grössere Erweiterungen oder Anpassungen erfährt, macht die Umstellung Sinn.
Damit die zusätzlichen Warnungen auch alle wirklich bearbeitet werden, sollte spätestens beim CI Build mit “Warnings as Errors” gearbeitet werden. Das kann auch ausschliesslich für die zusätzlichen Warnungen umgesetzt werden. Diese braucht man nicht alle aufzulisten und kann stattdessen einfach den Begriff “Nullable” verwenden.
“Nullable Reference Types” haben einen weiteren wichtigen Vorteil. Indem aus der Deklaration der Referenz hervorgeht, ob die Referenz nullable sein soll oder nicht, wird der Code besser verständlich und lässt sich einfacher erweitern.