Principy nullability
Pokud jste vývojář .NET, je pravděpodobné, že jste narazili na System.NullReferenceException. K tomu dochází v době běhu, když null
je dereferenced; to znamená, že když je proměnná vyhodnocena za běhu, ale proměnná odkazuje na null
. Tato výjimka je zdaleka nejčastější výjimkou v ekosystému .NET. Tvůrce null
, Sir Tony Hoare, se označuje null
jako "miliarda dolarů chyba".
V následujícím příkladu FooBar
je proměnná přiřazena null
a okamžitě se dereferenced, čímž dochází k problému:
// 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);
Problém se stává mnohem obtížnější odhalit jako vývojář, když se vaše aplikace zvětšují a zkomplikují. Zobrazení potenciálních chyb, jako je tato, je úlohou pro nástroje a kompilátor jazyka C# vám pomůže.
Definování zabezpečení null
Pojem zabezpečení null definuje sadu funkcí specifických pro typy s možnou hodnotou null, které pomáhají snížit počet možných NullReferenceException
výskytů.
Vzhledem k předchozímu FooBar
příkladu NullReferenceException
byste se mohli vyhnout kontrole, jestli fooBar
byla null
proměnná před jeho zrušením:
// 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);
Kompilátor může při identifikaci podobných scénářů odvodit záměr vašeho kódu a vynutit požadované chování. To je však pouze v případě, že je povolen kontext s možnou hodnotou null. Než probereme kontext s možnou hodnotou null, popíšeme možné typy s možnou hodnotou null.
Typy s povolenou hodnotou Null
Před jazykem C# 2.0 byly pouze odkazové typy s možnou hodnotou null. Typy hodnot, například int
nebo DateTime
nešlo.null
Pokud jsou tyto typy inicializovány bez hodnoty, vrátí se k jejich default
hodnotě. V případě int
, to je 0
. Pro , DateTime
je DateTime.MinValue
to .
Odkazové typy, které se vytvoří instance bez počátečních hodnot, fungují odlišně. Hodnota default
pro všechny odkazové typy je null
.
Vezměte v úvahu následující fragment kódu jazyka C#:
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
V předchozím příkladu:
first
jenull
to proto, že byl deklarován typstring
odkazu, ale nebyl proveden žádný přiřazení.second
je přiřazenastring.Empty
, když je deklarována. Objekt nikdy nemělnull
přiřazení.third
0
navzdory nepřiřazování. Je tostruct
(hodnota-typ) a mádefault
hodnotu0
.date
je neinicializován, ale jehodefault
hodnota je System.DateTime.MinValue.
Počínaje jazykem C# 2.0 můžete definovat typy hodnot s možnou hodnotou null pomocí Nullable<T>
(nebo T?
pro zkratku). To umožňuje, aby typy hodnot měly hodnotu null. Vezměte v úvahu následující fragment kódu jazyka C#:
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
V předchozím příkladu:
first
jenull
to proto, že typ hodnoty s možnou hodnotou null není inicializován.second
je přiřazenanull
, když je deklarována.third
jenull
hodnotadefault
proNullable<int>
hodnotunull
.fourth
je0
jakonew()
výraz voláNullable<int>
konstruktor aint
je0
ve výchozím nastavení.
Jazyk C# 8.0 zavedl odkazové typy s možnou hodnotou null, kde můžete vyjádřit svůj záměr, že odkazový typ může být null
nebo je vždy ne-null
. Možná si myslíte, že "Myslel jsem, že všechny odkazové typy jsou nullable!" Ty se mýlíš a oni jsou. Tato funkce umožňuje vyjádřit svůj záměr, který se kompilátor pak pokusí vynutit. Stejná T?
syntaxe vyjadřuje, že typ odkazu má být nullable.
Vezměte v úvahu následující fragment kódu jazyka C#:
#nullable enable
string first = string.Empty;
string second;
string? third;
V předchozím příkladu kompilátor odvodí váš záměr následujícím způsobem:
first
není nikdynull
tak, jak je to rozhodně přiřazeno.second
by nikdy neměl býtnull
, i když to je původněnull
.second
Vyhodnocením před přiřazením hodnoty dojde k upozornění kompilátoru, protože není inicializováno.third
může býtnull
. Může například odkazovat na ,System.String
ale může odkazovat nanull
. Některé z těchto variant jsou přijatelné. Kompilátor vám pomůže tím, že vás upozorní, pokud se dereferencethird
bez první kontroly, že není null.
Důležité
Aby bylo možné použít funkci odkazových typů s možnou hodnotou null, jak je znázorněno výše, musí být v kontextu s možnou hodnotou null. Toto je podrobně popsáno v další části.
Kontext s možnou hodnotou null
Kontexty s možnou hodnotou null umožňují jemně odstupňovaný ovládací prvek, jak kompilátor interpretuje proměnné referenčního typu. Existují čtyři možné kontexty s možnou hodnotou null:
disable
: Kompilátor se chová podobně jako C# 7.3 a starší.enable
: Kompilátor povolí všechny referenční analýzy null a všechny jazykové funkce.warnings
: Kompilátor provádí všechny analýzy null a generuje upozornění, když kód může dereferencenull
.annotations
: Kompilátor neprovádí analýzu null nebo generuje upozornění, když kód může dereferencenull
, ale můžete stále opatřit poznámkami kódu pomocí referenčních typů?
s možnou hodnotou null a operátorů odpustit od verze null (!
).
Tento modul je vymezený na disable
enable
kontexty s možnou hodnotou null. Další informace najdete v referenčních typech odkazů s možnou hodnotou Null: kontexty s možnou hodnotou Null.
Povolit odkazové typy s možnou hodnotou null
V souboru projektu C# (.csproj) přidejte podřízený <Nullable>
uzel do elementu <Project>
(nebo připojte k existujícímu <PropertyGroup>
). Tím se použije kontext s možnou enable
hodnotou null pro celý projekt.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- Omitted for brevity -->
</Project>
Případně můžete nastavit rozsah kontextu s možnou hodnotou null na soubor jazyka C# pomocí direktivy kompilátoru.
#nullable enable
Předchozí direktiva kompilátoru jazyka C# je funkčně ekvivalentní konfiguraci projektu, ale je vymezena na soubor, ve kterém se nachází. Další informace najdete v tématu Odkazové typy s možnou hodnotou Null: Kontexty s možnou hodnotou Null (docs)
Důležité
Kontext s možnou hodnotou null je ve výchozím nastavení povolen v souboru .csproj ve všech šablonách projektů jazyka C# počínaje rozhraním .NET 6.0 a vyšším.
Pokud je povolený kontext s možnou hodnotou null, zobrazí se nová upozornění. Představte si předchozí FooBar
příklad, který obsahuje dvě upozornění při analýze v kontextu s možnou hodnotou null:
Řádek
FooBar fooBar = null;
obsahuje upozornění nanull
přiřazení: Upozornění C# CS8600: Převod literálu null nebo možné hodnoty null na nenulový typ.Řádek
_ = fooBar.ToString();
má také upozornění. Tentokrát se kompilátor obává, žefooBar
může mít hodnotu null: Upozornění C# CS8602: Dereference pravděpodobně nulového odkazu.
Důležité
Neexistuje žádná zaručená bezpečnost null, i když reagujete na a eliminujete všechna upozornění. Existují některé omezené scénáře, které budou předávat analýzu kompilátoru, ale výsledkem modulu runtime NullReferenceException
.
Shrnutí
V této lekci jste se naučili povolit kontext s možnou hodnotou null v jazyce C#, který pomáhá chránit NullReferenceException
proti . V další lekci se dozvíte více o explicitní vyjádření záměru v kontextu s možnou hodnotou null.