Vyjádření záměru
V předchozí lekci jste se dozvěděli, jak kompilátor jazyka C# může provádět statickou analýzu, která pomáhá chránit NullReferenceException
proti . Dozvěděli jste se také, jak povolit kontext s možnou hodnotou null. V této lekci se dozvíte více o explicitní vyjádření záměru v kontextu s možnou hodnotou null.
Deklarace proměnných
S povoleným kontextem s možnou hodnotou null máte lepší přehled o tom, jak kompilátor vidí váš kód. Můžete reagovat na upozornění vygenerovaná z kontextu s povolenou hodnotou null a přitom explicitně definujete své záměry. Pojďme například pokračovat v prozkoumání FooBar
kódu a prozkoumání deklarace a přiřazení:
// Define as nullable
FooBar? fooBar = null;
Všimněte si přidaného ?
do FooBar
. To kompilátoru říká, že explicitně máte v úmyslu fooBar
mít hodnotu null. Pokud nemáte v úmyslu fooBar
mít hodnotu null, ale přesto se chcete upozornění vyhnout, zvažte následující:
// Define as non-nullable, but tell compiler to ignore warning
// Same as FooBar fooBar = default!;
FooBar fooBar = null!;
Tento příklad přidá operátor null-forgiving (!
), který null
dává kompilátoru pokyn, že explicitně inicializujete tuto proměnnou jako null. Kompilátor nebude vydávat upozornění na tento odkaz s hodnotou null.
Osvědčeným postupem je přiřadit proměnnénull
bez hodnoty null, pokud jsou deklarovány, pokud je to možné:
// Define as non-nullable, assign using 'new' keyword
FooBar fooBar = new(Id: 1, Name: "Foo");
Operátory
Jak je popsáno v předchozí lekci, jazyk C# definuje několik operátorů, které vyjadřují váš záměr ohledně typů odkazů s možnou hodnotou null.
Operátor null-forgiving (!
)
V předchozí části jste se seznámili s operátorem null-forgiving (!
). Říká kompilátoru, aby ignoroval upozornění CS8600. To je jeden ze způsobů, jak kompilátoru říct, že víte, co děláte, ale přináší upozornění, že byste měli vědět, co děláte!
Při inicializaci nenulových typů v době, kdy je povolen kontext s možnou hodnotou null, může být nutné explicitně požádat kompilátor o odpuštění. Představte si například následující kód:
#nullable enable
using System.Collections.Generic;
var fooList = new List<FooBar>
{
new(Id: 1, Name: "Foo"),
new(Id: 2, Name: "Bar")
};
FooBar fooBar = fooList.Find(f => f.Name == "Bar");
// The FooBar type definition for example.
record FooBar(int Id, string Name);
V předchozím příkladu FooBar fooBar = fooList.Find(f => f.Name == "Bar");
vygeneruje upozornění CS8600, protože Find
může vrátit null
. To by bylo možné null
přiřadit fooBar
, což je v tomto kontextu nenulové. V tomto nepomýšleném příkladu však víme, že Find
se nikdy nevrátí null
jako napsaný. Tento záměr můžete vyjádřit kompilátoru pomocí operátoru null-forgiving:
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!;
Poznamenejte si na !
konci .fooList.Find(f => f.Name == "Bar")
To kompilátoru říká, že víte, že objekt vrácený metodou Find
může být null
a je v pořádku.
Operátor null-forgiving můžete použít na objekt vložený před voláním metody nebo vyhodnocením vlastnosti. Představte si jiný příklad:
List<FooBar>? fooList = FooListFactory.GetFooList();
// Declare variable and assign it as null.
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!; // generates warning
static class FooListFactory
{
public static List<FooBar>? GetFooList() =>
new List<FooBar>
{
new(Id: 1, Name: "Foo"),
new(Id: 2, Name: "Bar")
};
}
// The FooBar type definition for example.
record FooBar(int Id, string Name);
V předchozím příkladu:
GetFooList
je statická metoda, která vrací typ s možnou hodnotou null,List<FooBar>?
.fooList
je přiřazena hodnota vrácenáGetFooList
hodnotou .- Kompilátor vygeneruje upozornění,
fooList.Find(f => f.Name == "Bar");
protože hodnota přiřazenáfooList
může býtnull
. - Za předpokladu
fooList
,null
že není ,Find
může se vrátitnull
, ale víme, že to nebude, takže se použije operátor null-odpustit.
Pokud chcete upozornění zakázat, můžete použít operátor fooList
null-forgiving:
FooBar fooBar = fooList!.Find(f => f.Name == "Bar")!;
Poznámka:
Operátor null-forgiving byste měli použít uvážlivě. Když ji jednoduše použijete k zavření upozornění, znamená to, že kompilátoru říkáte, aby vám nepomohl zjistit možnou chybnou hodnotu null. Používejte ji střídmě a jen tehdy, když jste si jistí.
Další informace najdete v tématu ! (null-forgiving) – operátor (referenční dokumentace jazyka C#).
Operátor null-coalescing (??
)
Při práci s typy s možnou hodnotou null možná budete muset vyhodnotit, jestli jsou aktuálně null
dostupné, a provést určitou akci. Pokud je například přiřazený null
typ s možnou hodnotou null nebo je neinicializován, budete je možná muset přiřadit nenulovou hodnotu. To je místo, kde je užitečný operátor nulového sjednocení (??
).
Představte si následující příklad:
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
salesTax ??= DefaultStateSalesTax.Value;
// Safely use salesTax object.
}
V předchozím kódu jazyka C#:
- Parametr
salesTax
je definován jako nullableIStateSalesTax
. - V těle
salesTax
metody je podmíněně přiřazen pomocí operátoru null-coalescing.- Tím se zajistí, že pokud
salesTax
byla předána, protoženull
bude mít hodnotu.
- Tím se zajistí, že pokud
Tip
Toto je funkčně ekvivalentní následujícímu kódu jazyka C#:
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
if (salesTax is null)
{
salesTax = DefaultStateSalesTax.Value;
}
// Safely use salesTax object.
}
Tady je příklad jiného běžného idiomu jazyka C#, kde může být užitečný operátor nulového sjednocení:
public sealed class Wrapper<T> where T : new()
{
private T _source;
// If given a source, wrap it. Otherwise, wrap a new source:
public Wrapper(T source = null) => _source = source ?? new T();
}
Předchozí kód jazyka C#:
- Definuje obecnou třídu obálky, kde je parametr obecného typu omezen na
new()
. - Konstruktor přijímá
T source
parametr, který je ve výchozím nastavenínull
. - Obálka
_source
je podmíněně inicializovánanew T()
na .
Další informace najdete v tématu ?? a ?? = operátory (referenční dokumentace jazyka C#).
Operátor s podmínkou null (?.
)
Při práci s typy s možnou hodnotou null může být nutné podmíněně provádět akce na základě stavu objektu null
. Příklad: v předchozí lekci FooBar
se záznam použil k předvedení NullReferenceException
dereferencingem null
. To bylo způsobeno tím, že byl ToString
volána. Podívejte se na stejný příklad, ale teď použijte operátor s podmínkou null:
using System;
// Declare variable and assign it as null.
FooBar fooBar = null;
// Conditionally dereference variable.
var str = fooBar?.ToString();
Console.Write(str);
// The FooBar type definition.
record FooBar(int Id, string Name);
Předchozí kód jazyka C#:
- Podmíněně dereference
fooBar
, přiřaďte výsledekToString
proměnnéstr
.- Proměnná
str
je typustring?
(řetězec s možnou hodnotou null).
- Proměnná
- Zapíše hodnotu do standardního
str
výstupu, což není nic. - Volání
Console.Write(null)
je platné, takže neexistují žádná upozornění. - Pokud byste chtěli volat
Console.Write(str.Length)
, zobrazilo by se upozornění, protože by se mohlo stát, že byste mohli odvodit hodnotu null.
Tip
Toto je funkčně ekvivalentní následujícímu kódu jazyka C#:
using System;
// Declare variable and assign it as null.
FooBar fooBar = null;
// Conditionally dereference variable.
string str = (fooBar is not null) ? fooBar.ToString() : default;
Console.Write(str);
// The FooBar type definition.
record FooBar(int Id, string Name);
Operátor můžete zkombinovat a vyjádřit tak váš záměr. Můžete například zřetězit operátory a ??
operátory?.
:
FooBar fooBar = null;
var str = fooBar?.ToString() ?? "unknown";
Console.Write(str); // output: unknown
Další informace najdete v operátorech ?. a ?[] (s podmínkou null).
Shrnutí
V této lekci jste se dozvěděli o vyjádření záměru nullability v kódu. V další lekci použijete to, co jste se naučili u existujícího projektu.