Grundlegendes zur Nullzulässigkeit
Wenn Sie .NET-Entwickler sind, ist die Wahrscheinlichkeit hoch, dass Ihnen die System.NullReferenceException bereits begegnet ist. Diese tritt zur Laufzeit auf, wenn eine null dereferenziert wird – also, wenn eine Variable zur Laufzeit ausgewertet wird, die Variable jedoch auf null verweist. Diese Ausnahme ist die bei weitem am häufigsten auftretende Ausnahme innerhalb des .NET-Ökosystems. Der Schöpfer von null, Sir Tony Hoare, bezeichnet null als den „Milliarden-Dollar-Fehler“.
Im folgenden Beispiel wird die FooBar-Variable null zugewiesen und sofort dereferenziert, wodurch das Problem offen zutage tritt:
// Declare variable and assign it as null.
FooBar fooBar = null;
// Dereference variable by calling ToString.
// This will throw a NullReferenceException.
_ = fooBar.ToString();
// The FooBar type definition.
record FooBar(int Id, string Name);
Das Problem wird für Entwickler viel schwieriger zu erkennen, wenn Ihre Apps größer und komplexer werden. Das Aufspüren potenzieller Fehler wie dieses ist eine Aufgabe für Tools, und der C#-Compiler steht bereit, um zu helfen.
Definieren von Nullsicherheit
Der Begriff Nullsicherheit definiert eine Reihe von Merkmalen, die für Nullable-Typen spezifisch sind, mit denen sich die Anzahl der möglichen NullReferenceException-Vorkommen verringern lässt.
Beim Betrachten des vorstehenden FooBar-Beispiels wird klar, dass die NullReferenceException vermieden werden könnte, wenn vor der Dereferenzierung überprüft würde, ob die fooBar-Variable den Wert null hat:
// Declare variable and assign it as null.
FooBar fooBar = null;
// Check for null
if (fooBar is not null)
{
_ = fooBar.ToString();
}
// The FooBar type definition for example.
record FooBar(int Id, string Name);
Zur Unterstützung bei der Identifizierung von Szenarien wie diesem kann der Compiler die Absicht Ihres Codes ableiten und das gewünschte Verhalten erzwingen. Dies gilt jedoch nur, wenn ein nullbarer Kontext aktiviert ist. Bevor wir uns mit dem Nullwerte zulassenden Kontext befassen, lassen Sie uns die möglichen Nullwerte zulassenden Typen beschreiben.
Nullwerte zulassende Typen
Vor C# 2.0 ließen nur Verweistypen Nullwerte zu. Werttypen wie int oder DateTimekonnten nichtnullsein. Wenn diese Typen ohne Wert initialisiert werden, greifen sie auf ihren default-Wert zurück. Im Fall von eines int ist dies 0. Bei einem DateTime ist dies DateTime.MinValue.
Verweistypen, die ohne Anfangswerte instanziiert werden, funktionieren anders. Der default-Wert für alle Verweistypen ist null.
Betrachten Sie den folgenden C#-Codeausschnitt:
string first; // first is null
string second = string.Empty // second is not null, instead it's an empty string ""
int third; // third is 0 because int is a value type
DateTime date; // date is DateTime.MinValue
Im vorherigen Beispiel:
firstistnull, weil der Verweistypstringdeklariert, aber keine Zuweisung vorgenommen wurde.secondwirdstring.Emptybei der Deklaration zugewiesen. Für das Objekt gab es zu keiner Zeit einenull-Zuweisung.thirdist0, obwohl keine Zuweisung erfolgt ist. Es handelt sich um einenstruct(Werttyp) und hat dendefault-Wert0.dateist nicht initialisiert, aber seindefault-Wert ist System.DateTime.MinValue.
Seit C# 2.0 können Sie Nullable-Werttypen mit Nullable<T> (oder T? kurz) definieren. Dies ermöglicht es, Werttypen nullable zu machen. Betrachten Sie den folgenden C#-Codeausschnitt:
int? first; // first is implicitly null (uninitialized)
int? second = null; // second is explicitly null
int? third = default; // third is null as the default value for Nullable<Int32> is null
int? fourth = new(); // fourth is 0, since new calls the nullable constructor
Im vorherigen Beispiel:
firstistnull, da der Nullable-Werttyp nicht initialisiert ist.secondwirdnullbei der Deklaration zugewiesen.thirdistnull, da derdefault-Wert fürNullable<int>nulllautet.fourthist0, da der Ausdrucknew()denNullable<int>-Konstruktor aufruft undintstandardmäßig0ist.
In C# 8.0 wurden Nullable-Verweistypen eingeführt, in denen Sie Ihre Absicht ausdrücken können, dass ein Verweistyp möglicherweisenull ist oder immer Nicht-null ist. Sie denken vielleicht: „Ich war der Überzeugung, dass alle Verweistypen Nullwerte zulassen“. Da liegen Sie nicht falsch, und ja, sie tun das. Mit diesem Feature können Sie Ihre Absicht ausdrücken, die der Compiler dann durchzusetzen versucht. Die gleiche T?-Syntax drückt aus, dass ein Verweistyp darauf abzielt, nullfähig zu sein.
Betrachten Sie den folgenden C#-Codeausschnitt:
#nullable enable
string first = string.Empty;
string second;
string? third;
Unter Zugrundelegung des vorherigen Beispiels leitet der Compiler Ihre Absicht wie folgt ab:
firstist nienull, da es definitiv zugewiesen ist.secondsollte nienullsein, obwohl es anfänglichnullist. Das Auswerten vonsecondvor dem Zuweisen eines Werts führt zu einer Compilerwarnung, da es nicht initialisiert ist.thirdist möglicherweisenull. Beispielsweise verweist es möglicherweise auf einenSystem.String, möglicherweise aber auch aufnull. Beide dieser Varianten sind akzeptabel. Der Compiler unterstützt Sie, indem er Sie warnt, wenn Siethirddereferenzieren, ohne zuerst zu überprüfen, ob er nicht null ist.
Wichtig
Um das Feature der Nullwerte zulassenden Verweistypen wie oben gezeigt zu verwenden, müssen sie sich innerhalb eines Nullwerte zulassenden Kontexts befinden. Dies wird im nächsten Abschnitt ausführlich beschrieben.
Nullwerte zulassender Kontext
Nullable-Kontexte ermöglichen eine differenzierte Steuerung der Interpretation von Verweistypvariablen durch den Compiler. Es gibt vier mögliche Nullable-Kontexte:
disable: Der Compiler verhält sich ähnlich wie in C# 7.3 und früheren Versionen.enable: Der Compiler aktiviert alle Nullverweisanalysen und alle Sprachfeatures.warnings: Der Compiler führt alle Nullverweisanalysen durch und gibt Warnungen aus, wenn der Code möglicherweisenulldereferenziert.annotations: Der Compiler führt keine Nullwertanalyse durch und gibt keine Warnungen aus, wenn Codenulldereferenzieren könnte, aber Sie können Ihren Code trotzdem mithilfe von null-fähigen Verweistypen?und nullverzeihenden Operatoren!annotieren.
Dieses Modul ist auf den Umfang der Nullwerte zulassenden Kontexte disable oder enable festgelegt. Weitere Informationen finden Sie unter Nullable-Verweistypen: Nullable-Kontexte.
Aktivieren von Nullwerte zulassenden Verweistypen
Fügen Sie in der C#-Projektdatei (CSPROJ) dem <Nullable>-Element einen untergeordneten <Project>-Knoten hinzu (oder fügen Sie an eine vorhandene <PropertyGroup> an). Dadurch wird der nullable Kontext enable auf das gesamte Projekt angewendet.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- Omitted for brevity -->
</Project>
Alternativ können Sie den Nullable-Kontext mit einer Compileranweisung auf den Umfang einer C#-Datei beschränken.
#nullable enable
Die vorstehende C#-Compileranweisung entspricht funktional der Projektkonfiguration, ihr Umfang ist jedoch auf die Datei beschränkt, in der sie sich befindet. Weitere Informationen finden Sie unter Nullable-Verweistypen: Nullable-Kontexte (Dokumentation).
Wichtig
Der Nullable-Kontext ist in der .csproj Datei in allen C#-Projektvorlagen ab .NET 6.0 und höher standardmäßig aktiviert.
Wenn der Nullwerte zulassende Kontext aktiviert ist, erhalten Sie neue Warnungen. Betrachten Sie das vorangehende FooBar-Beispiel – es enthält zwei Warnungen, wenn es in einem Nullable-Kontext analysiert wird.
Die
FooBar fooBar = null;-Zeile enthält eine Warnung für dienull-Zuweisung: C#-Warnung CS8600: Das NULL-Literal oder ein möglicher NULL-Wert wird in einen Non-Nullable-Typ konvertiert.Die
_ = fooBar.ToString();-Zeile enthält ebenfalls eine Warnung. Dieses Mal hat der Compiler Bedenken, dassfooBarmöglicherweise null ist: C# Warning CS8602: Dereference of a possibly null reference (C#-Warnung CS8602: Dereferenzierung eines potenziellen Nullverweises).
Wichtig
Es gibt keine garantierte Nullsicherheit, selbst wenn Sie auf alle Warnungen reagieren und diese beseitigen. Es gibt einige eingeschränkte Szenarios, die die Analyse des Compilers bestehen und dennoch zu einer NullReferenceException zur Laufzeit führen.
Zusammenfassung
In dieser Lerneinheit haben Sie gelernt, einen nullfähigen Kontext in C# zu aktivieren, um vor NullReferenceException zu schützen. In der nächsten Lerneinheit erfahren Sie mehr über das explizite Ausdrücken Ihrer Absicht in einem Nullwerte zulassenden Kontext.

