Operátory null jazyka C#

Tip

Tento článek je součástí části Základy pro vývojáře, kteří znají aspoň jeden programovací jazyk a učí se jazyk C#. Pokud s programováním teprve začínáte, začněte nejprve kurzy Začínáme . Úplný přehled operátorů naleznete v tématu Operátory přístupu k členům a operátory nulového sjednocení v jazykové referenci.

Jazyk C# poskytuje několik operátorů, které umožňují bezpečnou manipulaci s kódem obsahujícím null hodnoty. Místo vnoření if (x != null) strážců v celém vašem kódu umožňují tyto operátory vyjádřit bezpečný přístup při práci s null hodnotami, náhradní hodnoty a testy null v jednom výrazu.

Tento článek popisuje ?. a ?[] pro podmíněný přístup s hodnotou null, ?? pro přiřazování hodnot null a ??= pro porovnávání is null/is not null vzorů s hodnotou null.

Přístup k členu s podmínkou null ?.

Operátor ?. přistupuje k členu pouze v případech, kdy objekt není null. Pokud je nullobjekt , celý výraz se vyhodnotí jako null místo vyvolání NullReferenceException:

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

Operátor ?.zkratuje: když je nulllevá strana , vše vpravo se přeskočí. Žádná volání metod nejsou spuštěna a nedochází k žádným vedlejším účinkům.

V jednom výrazu můžete zřetězit více ?. operátorů. Řetězec se zastaví na prvním null místě, na který narazí:

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

Přístup k nulovému podmíněnému indexeru ?[]

Operátor ?[] použije stejné chování zkratového okruhu pro indexer a přístup k poli. Použijte to, když kolekce samotná může být null.

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

Řetězení null-ově podmíněných operátorů

Zřetězte více ?. operátorů a projdete cestou potenciálně nulových odkazů. Řetěz se zkratuje při prvním null:

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

Pokud Customer je null, ani Address ani City se nevyhodnocuje. Celý výraz vrací null.

Vyvolání delegáta bezpečného pro přístup z více vláken

?. poskytuje čistý a bezpečný pro vlákna způsob, jak vytvořit delegáta nebo vyvolat událost. Výraz delegáta se vyhodnocuje jenom jednou, takže neexistuje žádné okno pro odhlášení odběru jiného vlákna mezi kontrolou null a vyvoláním:

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!

Tento vzor nahrazuje starší if (clicked != null) clicked(...) idiom.

Souhlásání s hodnotou null ??

Operátor ?? vrátí levý operand, pokud není null, a jeho pravý operand, když je nullvlevo . Použijte ji k zadání výchozí hodnoty:

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

?? je pravostranně asociativní, a proto se a ?? b ?? c vyhodnotí jako a ?? (b ?? c). První hodnota, která není null, vyhrává. Běžným vzorem je zřetězit ?. s ??: použijte ?. k bezpečnému procházení řetězu s možnou hodnotou null, a pak ?? nahraďte výchozí hodnotu, pokud se řetězec vrátí null. Úplný příklad naleznete v tématu Kombinování operátorů null.

Přiřazení slučovací s hodnotou null ??=

Operátor ??= přiřadí pravou hodnotu proměnné pouze v případech, kdy je nullproměnná . Použijte ji k opožděné inicializaci:

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"];

Výraz na pravé straně se vyhodnocuje pouze tehdy, když je proměnná null. Pokud už proměnná obsahuje hodnotu, pravá strana se nevyhodnotí vůbec.

Podmíněné přiřazení s hodnotou null (C# 14)

Počínaje jazykem C# 14 můžete použít ?. a ?[] jako cíle přiřazení. Přiřazení se spustí pouze v případech, kdy je levý objekt nenulový:

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)

Pravá strana se vyhodnocuje pouze tehdy, když je známé, že hodnota na levé straně není null.

Porovnávání vzorů s hodnotou Null: is null a is not null

is null Vzory is not null testují, jestli je nullvýraz:

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.");
}

Upřednostněte is null před == null při kontrole null. Operátor == může být přetížen, což může znamenat, že se vrátí x == null i tehdy, když true není x, pokud typ definuje vlastní operátor rovnosti. Vzor is null vždy testuje skutečný odkaz null bez ohledu na přetížení operátoru.

string? value = "hello";

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

Kombinování operátorů null

V praxi často kombinujete několik těchto operátorů. Jeden výraz může bezpečně procházet hlubokým grafem objektů, použít náhradní hodnotu a pak zkontrolovat výsledek.

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.

Operátor ignorující nulovou hodnotu !

Operátor ! přípony potlačí upozornění s možnou hodnotou null. Připojte !, aby kompilátoru sdělil, že tento výraz určitě nemá hodnotu null. Operátor nemá žádný efekt za běhu programu. Má vliv pouze na analýzu stavu null kompilátoru.

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

Používejte ! střídmě a pouze v případě, že máte informace, které kompilátor neobsahuje. Mezi příklady patří testy, které záměrně předávají null za účelem otestování logiky kontroly argumentů, nebo volání metody, jejíž kontrakt zaručuje vrácení nenulové hodnoty pro známý vstup. Nadměrné použití ! oslabuje účel nulovatelných odkazových typů. Úplné vysvětlení najdete v tématu Odkazové typy s možnou hodnotou Null.

Viz také