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 Nullwerte zulassender Kontext aktiviert ist. Bevor wir uns mit dem Nullwerte zulassenden Kontext befassen, lassen Sie uns die möglichen Nullable-Typen beschreiben.
Nullable-Typen
Vor C# 2.0 ließen nur Verweistypen Nullwerte zu. Werttypen wie int
oder DateTime
können nicht null
sein. 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:
first
istnull
, weil der Verweistypstring
deklariert, aber keine Zuweisung vorgenommen wurde.second
wirdstring.Empty
bei der Deklaration zugewiesen. Für das Objekt gab es zu keiner Zeit einenull
-Zuweisung.third
ist0
, obwohl keine Zuweisung erfolgt ist. Es ist einestruct
(Werttyp) und hat dendefault
-Wert0
.date
ist 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, dass Werttypen Nullwerte zulassen. 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:
first
istnull
, da der Nullable-Werttyp nicht initialisiert ist.second
wirdnull
bei der Deklaration zugewiesen.third
istnull
, da derdefault
-Wert fürNullable<int>
null
lautet.fourth
ist0
, da der Ausdrucknew()
denNullable<int>
-Konstruktor aufruft undint
standardmäßig0
ist.
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 hatte das so verstanden, dass alle Verweistypen Nullwerte zulassen.“ Das stimmt auch. Mit diesem Feature können Sie Ihre Absicht ausdrücken, die der Compiler dann durchzusetzen versucht. Die gleiche T?
-Syntax gibt an, dass ein Verweistyp Nullwerte zulassend sein soll.
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:
first
ist nienull
, da es definitiv zugewiesen ist.second
sollte nienull
sein, obwohl es anfänglichnull
ist. Das Auswerten vonsecond
vor dem Zuweisen eines Werts führt zu einer Compilerwarnung, da es nicht initialisiert ist.third
ist 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 Siethird
dereferenzieren, ohne zuerst zu überprüfen, ob er nicht null ist.
Wichtig
Um das Feature der Nullable-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.
Nullable-Kontext
Nullable-Kontexte ermöglichen eine differenzierte Steuerung der Interpretation von Verweistypvariablen durch den Compiler. Es gibt vier mögliche Kontexte, die Nullwerte zulassen:
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öglicherweisenull
dereferenziert.annotations
: Der Compiler führt keine Nullverweisanalyse durch und gibt keine Warnungen aus, wenn Codenull
dereferenzieren könnte, Sie können Ihren Code aber trotzdem mithilfe von Nullwerte zulassenden Verweistypen?
und nulltoleranten Operatoren!
kommentieren.
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 Nullable-Verweistypen
Fügen Sie in der C#-Projektdatei (CSPROJ) dem <Project>
-Element einen untergeordneten <Nullable>
-Knoten hinzu (oder fügen Sie an eine vorhandene <PropertyGroup>
an). Dadurch wird der Nullwerte zulassend e 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 Umfang des Nullable-Kontexts mithilfe einer Compileranweisung auf eine C#-Datei anwenden.
#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 Nullwerte zulassende Kontext ist in der CSPROJ-Datei standardmäßig in allen C#-Projektvorlagen ab .NET 6.0 und höher 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 Kontext analysiert wird, in dem Nullwerte zugelassen werden:
Die
FooBar fooBar = null;
-Zeile enthält eine Warnung für dienull
-Zuweisung: C# Warning CS8600: Converting null literal or possible null value to non-nullable type. (C#-Warnung CS8600: Konvertieren eines Null-Literals oder eines möglichen Nullwerts in einen non-Nullable-Typ).Die
_ = fooBar.ToString();
-Zeile enthält ebenfalls eine Warnung. Dieses Mal hat der Compiler Bedenken, dassfooBar
mö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 Nullwerte zulassenden Kontext in C# zu aktivieren, um einen Schutz vor NullReferenceException
aufzubauen. In der nächsten Lerneinheit erfahren Sie mehr über das explizite Ausdrücken Ihrer Absicht in einem Nullwerte zulassenden Kontext.