C#-Nulloperatoren

Tipp

Dieser Artikel ist Teil des Abschnitts "Grundlagen" für Entwickler, die mindestens eine Programmiersprache kennen und C# erlernen. Wenn Sie noch nicht mit der Programmierung vertraut sind, beginnen Sie zuerst mit den Lernprogrammen " Erste Schritte ". Den vollständigen Operatorverweis finden Sie unter Operatoren für Mitgliedzugriff und Null-Zusammenknüpfungsoperatoren im Sprachverweis.

C# stellt mehrere Operatoren bereit, die nullsicheren Code prägnant machen. Anstelle der Verschachtelung von Schutzmechanismen if (x != null) im Code können Sie mithilfe dieser Operatoren nullsicheren Zugriff, Fallbackwerte und Nulltests in einem einzelnen Ausdruck verwenden.

In diesem Artikel wird ?. und ?[] der nullbedingten Zugriff ?? für nullkoalierend und ??= für nullkoalierende Zuweisungen und is null/is not null für den Nullmusterabgleich behandelt.

Null-bedingter Memberzugriff ?.

Der ?.-Operator greift nur auf ein Mitglied zu, wenn das Objekt nicht null ist. Wenn das Objekt null ist, wird der gesamte Ausdruck zu null ausgewertet, anstatt ein NullReferenceException auszulösen.

string? name = null;

// Without ?., accessing a member on null throws NullReferenceException:
// int len = name.Length; // throws if name is null

// ?. returns null instead of throwing:
int? len = name?.Length;
Console.WriteLine(len.HasValue); // False

name = "C#";
Console.WriteLine(name?.Length); // 2

Der Operator ?. führt einen Kurzschluss herbei: Wenn die linke Seite null ist, wird alles auf der rechten Seite übersprungen. Es werden keine Methodenaufrufe ausgeführt, und es treten keine Nebenwirkungen auf.

Sie können mehrere ?. Operatoren in einem einzelnen Ausdruck verketten. Die Kette stoppt an der ersten null, auf die sie trifft.

string? input = null;

// Chain ?. across multiple method calls — short-circuits at the first null:
string? upper = input?.Trim()?.ToUpperInvariant();
Console.WriteLine(upper ?? "(none)"); // (none)

input = "  hello  ";
Console.WriteLine(input?.Trim()?.ToUpperInvariant()); // HELLO

Null-bedingter Indexerzugriff ?[]

Der ?[] Operator wendet dasselbe Kurzschlussverhalten auf Indexer und Arrayzugriff an. Verwenden Sie es, wenn die Sammlung selbst möglicherweise null sein könnte.

string[]? tags = null;

// ?[] accesses an element only when the collection is non-null
string? first = tags?[0];
Console.WriteLine(first ?? "(none)"); // (none)

tags = ["csharp", "dotnet", "nullable"];
Console.WriteLine(tags?[0]);          // csharp

Kette nullbedingter Operatoren

Verketten Sie mehrere Operatoren ?., um einen Pfad mit potenziell Nullverweisen zu durchlaufen. Die Kette führt beim ersten null einen Kurzschluss herbei:

var order = new Order("ORD-001", null);

// Each ?. short-circuits when null: Customer is null, so Address and City are never accessed
string? city = order.Customer?.Address?.City;
Console.WriteLine(city ?? "(no city)"); // (no city)

var fullOrder = new Order("ORD-002",
    new Customer("Alice", new Address("123 Main St", "Springfield", "IL")));

Console.WriteLine(fullOrder.Customer?.Address?.City); // Springfield

Wenn Customernull ist, wird weder Address noch City ausgewertet. Der gesamte Ausdruck gibt zurück null.

Threadsicherer Delegataufruf

?. bietet eine saubere, threadsichere Möglichkeit zum Aufrufen eines Delegaten oder Auslösen eines Ereignisses. Der Delegatausdruck wird nur einmal ausgewertet, sodass es kein Fenster für einen anderen Thread gibt, um zwischen der Nullüberprüfung und dem Aufruf abbestellen zu können:

EventHandler? clicked = null;

// No subscribers — ?.Invoke does nothing instead of throwing NullReferenceException
clicked?.Invoke(null, EventArgs.Empty);

clicked += (_, _) => Console.WriteLine("Button clicked!");

// With a subscriber — ?.Invoke calls the handler
clicked?.Invoke(null, EventArgs.Empty);
// Output: Button clicked!

Dieses Muster ersetzt den älteren if (clicked != null) clicked(...) Idiom.

Nullkoalierende ??

Der Operator ?? gibt den linken Operanden zurück, wenn er nicht null ist, und den rechten Operanden, wenn der linke null ist. Verwenden Sie ihn, um einen Standardwert bereitzustellen:

string? username = null;

// ?? returns the right-hand value when the left-hand is null
string display = username ?? "Guest";
Console.WriteLine(display); // Guest

username = "alice";
display  = username ?? "Guest";
Console.WriteLine(display); // alice

?? ist rechtsassoziativ, daher wird a ?? b ?? c wie a ?? (b ?? c) ausgewertet. Der erste Nicht-Null-Wert gewinnt. Ein gängiges Muster besteht darin, ?. mit ?? zu verketten: Verwenden Sie ?., um eine potenziell null-beinhaltende Kette sicher zu durchlaufen, und dann ??, um einen Standardwert zu verwenden, falls die Kette null zurückgibt. Ein vollständiges Beispiel finden Sie unter Kombinieren von NULL-Operatoren.

Nullkoalierende Zuweisung ??=

Der ??= Operator weist einer Variablen nur dann den rechten Wert zu, wenn die Variable lautet null. Verwenden Sie sie für die faule Initialisierung:

List<string>? cache = null;

// ??= assigns only when the variable is null
cache ??= LoadData();
Console.WriteLine(cache.Count); // 3

// cache is already non-null, so LoadData() isn't called again
cache ??= LoadData();
Console.WriteLine(cache.Count); // 3

static List<string> LoadData() => ["alpha", "beta", "gamma"];

Der rechte Ausdruck wird nur ausgewertet, wenn die Variable null ist. Wenn die Variable bereits über einen Wert verfügt, wird die rechte Seite überhaupt nicht ausgewertet.

Null-bedingte Zuordnung (C# 14)

Ab C# 14 können Sie ?. und ?[] als Zuordnungsziele verwenden. Die Zuordnung wird nur ausgeführt, wenn das Objekt auf der linken Seite nicht null ist.

AppConfig? config = new AppConfig();

// Assigns only when config is non-null (C# 14)
config?.Theme = "dark";
Console.WriteLine(config?.Theme); // dark

AppConfig? missing = null;
missing?.Theme = "light";                         // no-op: missing is null
Console.WriteLine(missing?.Theme ?? "(no config)"); // (no config)

Die rechte Seite wird nur ausgewertet, wenn die linke Seite als nicht null bekannt ist.

Nullmusterabgleich: is null und is not null

Die Muster is null und is not null testen, ob ein Ausdruck null ist.

string? input = null;

// is null is the preferred test — unaffected by operator overloading
if (input is null)
{
    Console.WriteLine("No input provided.");
}

// == null also works, but a custom == operator can change its behavior
if (input == null)
{
    Console.WriteLine("Still no input.");
}

Bevorzugen Sie is null über == null für NULL-Überprüfungen. Der ==-Operator kann überladen werden, was bedeutet, dass x == null möglicherweise true zurückgibt, selbst wenn x nicht null ist, falls der Typ einen benutzerdefinierten Gleichheitsoperator definiert. Das is null Muster testet immer auf die tatsächliche null-Referenz, unabhängig von der Operatorüberladung.

string? value = "hello";

if (value is not null)
{
    Console.WriteLine(value.ToUpper()); // HELLO
}

Kombinieren von NULL-Operatoren

In der Praxis kombinieren Sie häufig mehrere dieser Operatoren. Ein Ausdruck kann ein tiefes Objektdiagramm sicher durchlaufen, einen Fallback anwenden und dann auf das Ergebnis schützen:

Order? order = GetPendingOrder();

// Chain ?. for safe traversal, ?? for a fallback, is null for a clear guard
string city = order?.Customer?.Address?.City ?? "unknown";

if (order is null)
{
    Console.WriteLine("No pending order.");
}
else
{
    Console.WriteLine($"Shipping to: {city}");
}
// Output: No pending order.

Nulltoleranter Operator !

Der Postfix-Operator ! unterdrückt nullbare Warnungen. Fügen Sie an ! , um dem Compiler mitzuteilen, dass dieser Ausdruck definitiv nicht NULL ist. Der Operator hat zur Laufzeit keine Auswirkung. Sie wirkt sich nur auf die Nullzustandsanalyse des Compilers aus.

string? name = FindUser("alice");

// Use ! only when you have information the compiler doesn't.
// FindUser guarantees a non-null result for known usernames.
int length = name!.Length;
Console.WriteLine(length); // 5

Verwenden Sie ! sparsam, und nur dann, wenn Sie Informationen haben, die der Compiler nicht hat. Beispiele sind Tests, bei denen absichtlich null übergeben wird, um die Logik der Argumentüberprüfung zu validieren, oder das Aufrufen einer Methode, deren Spezifikation eine nicht-null Rückgabe für eine bekannte Eingabe garantiert. Der übermäßige Gebrauch von ! verfehlt den Zweck von nullablen Verweistypen. Eine vollständige Erläuterung finden Sie unter Nullable-Referenztypen.

Siehe auch