Pochopení nullability
Pokud jste vývojář .NET, je pravděpodobné, že jste narazili na System.NullReferenceException. K tomu dochází v běhovém prostředí, když je null dereferencováno; to znamená, že když se proměnná vyhodnocuje 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, odkazuje na null jako "chyba za miliardu dolarů."
V následujícím příkladu je proměnná FooBar přiřazena k null a okamžitě dereferencována, čí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 byste se mohli vyhnout NullReferenceException tím, že zjistíte, zda proměnná fooBar byla null před jeho dereferencování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 nulový kontext. Než probereme kontext s možností null, popíšeme možné typy nullable.
Nullable typy
Před jazykem C# 2.0 mohly null hodnotu nabývat pouze odkazové typy. Typy hodnot, například int nebo DateTimenešlo být 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 to DateTime.MinValue.
Odkazové typy, které jsou instancovány 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:
-
firstjenullprotože byl deklarován typ odkazustring, ale nebylo provedeno žádné přiřazení. -
secondje přiřazenostring.Empty, když je deklarováno. Objekt nikdy nemělnullpřiřazení. -
thirdje0navzdory tomu, že není přiřazen. Je tostruct(hodnotový typ) a jehodefaulthodnota je0. -
dateje neinicializován, ale jehodefaulthodnota 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:
-
firstjenull, protože nulovatelný typ hodnoty není inicializován. -
secondje přiřazenonull, když je deklarováno. -
thirdjenulljakodefaultproNullable<int>, jenull. -
fourthje0, protože výraznew()volá konstruktorNullable<int>aintje0standardně.
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: „Myslím, že všechny odkazové typy mohou být null!“ Nemýlíte se, a mohou. Tato funkce umožňuje vyjádřit svůj záměr, který se kompilátor pak pokusí vynutit. Stejná syntaxe T? vyjadřuje, že typ odkazu může být nullován.
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:
-
firstnení nikdynulltak, jak je to rozhodně přiřazeno. -
secondby nikdy neměl býtnull, i když to je původněnull. Vyhodnocenísecondpřed přiřazením hodnoty vyvolá upozornění kompilátoru, jelikož není inicializováno. -
thirdmůže býtnull. Například může odkazovat na , ale také může odkazovat naSystem.String. Některé z těchto variant jsou přijatelné. Kompilátor vám pomůže tím, že vás upozorní, pokud odkazujete nathirdaniž byste nejprve zkontrolovali, že není null.
Důležité
Aby bylo možné použít funkci možných odkazových typů s 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 nulovatelný
Nullable kontexty umožňují jemné řízení toho, jak kompilátor interpretuje proměnné referenčního typu. Existují čtyři možné nullovatelné kontexty.
-
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 nulových hodnot a generuje upozornění, když kód může dereferencovatnull. -
annotations: Kompilátor neprovádí analýzu nulových hodnot ani negeneruje upozornění, když kód může dereferencovatnull, ale můžete stále annotovat svůj kód pomocí nulovatelných referenčních typů?a operátory ignorujícími nulové hodnoty (!).
Tento modul je vymezený buď na disable nebo na enable kontexty s možnou hodnotou null. Další informace najdete v odkazových typech s možnou hodnotou Null: kontexty s nulovými hodnotami.
Povolit referenční typy s možností 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 na Nulovatelné referenční typy: Nulovatelné kontexty (docs)
Důležité
Kontext s povolenými hodnotami null je ve výchozím nastavení povolen v souboru .csproj ve všech šablonách projektů jazyka C# počínaje verzí .NET 6.0.
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;má upozornění na přiřazenínull: 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á, žefooBarmůž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é projdou analýzou kompilátoru, ale vedou k chybě během běhu programu NullReferenceException.
Shrnutí
V této lekci jste se naučili povolit nulovatelný kontext v jazyce C#, který pomáhá chránit proti NullReferenceException. V další jednotce se dozvíte více o explicitním vyjádření vašeho záměru v nulovatelném kontextu.