Opis możliwości dopuszczania wartości null
Jeśli jesteś deweloperem platformy .NET, prawdopodobnie napotkasz element System.NullReferenceException. Dzieje się tak w czasie wykonywania, gdy null
element jest wyłuszony; oznacza to, że gdy zmienna jest obliczana w czasie wykonywania, ale zmienna odwołuje się do null
. Ten wyjątek jest zdecydowanie najczęściej występującym wyjątkiem w ekosystemie platformy .NET. Twórca null
, Sir Tony Hoare, nazywa się null
"błędem miliardów dolarów".
W poniższym przykładzie zmienna FooBar
jest przypisywana do null
zmiennej i natychmiast wyłuszczana, co powoduje wyświetlenie problemu:
// 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);
Problem staje się znacznie trudniejszy do wykrycia jako deweloper, gdy aplikacje rosną w rozmiarze i złożoności. Wykrycie potencjalnych błędów, takich jak to, jest zadaniem narzędzi, a kompilator języka C# jest tutaj, aby pomóc.
Definiowanie bezpieczeństwa o wartości null
Termin Bezpieczeństwo wartości null definiuje zestaw funkcji specyficznych dla typów dopuszczających wartość null, które pomagają zmniejszyć liczbę możliwych NullReferenceException
wystąpień.
Biorąc pod uwagę poprzedni FooBar
przykład, można uniknąć NullReferenceException
tego, sprawdzając, czy zmienna fooBar
była null
przed wyłuszeniem go:
// 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);
Aby ułatwić identyfikowanie takich scenariuszy, kompilator może wywnioskować intencję kodu i wymusić żądane zachowanie. Jest to jednak tylko wtedy, gdy jest włączony kontekst dopuszczalny do wartości null. Przed omówieniem kontekstu dopuszczalnego wartości null opiszmy możliwe typy dopuszczane do wartości null.
Typy dopuszczające wartości null
Przed C# 2.0 tylko typy odwołań były dopuszczane do wartości null. Typy wartości, takie jak int
lub DateTime
nie mogą być null
. Jeśli te typy są inicjowane bez wartości, wracają do ich default
wartości. W przypadku int
elementu , jest 0
to . W przypadku elementu DateTime
jest to DateTime.MinValue
.
Typy odwołań tworzone bez wartości początkowych działają inaczej. Wartość default
dla wszystkich typów odwołań to null
.
Rozważmy następujący fragment kodu w języku 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
W powyższym przykładzie:
first
jestnull
spowodowane tym, że typstring
odwołania został zadeklarowany, ale nie wykonano przypisania.second
jest przypisywanystring.Empty
po zadeklarowaniu. Obiekt nigdy nie miałnull
przypisania.third
mimo0
braku przypisania. Jest tostruct
(typ wartości) i madefault
wartość0
.date
jest niezainicjowany, ale jegodefault
wartość to System.DateTime.MinValue.
Począwszy od języka C# 2.0, można zdefiniować typy wartości dopuszczalnych wartości null przy użyciu ( Nullable<T>
lub T?
w skrócie). Dzięki temu typy wartości mogą mieć wartość null. Rozważmy następujący fragment kodu w języku 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
W powyższym przykładzie:
first
Jest tonull
spowodowane tym, że typ wartości dopuszczanej do wartości null jest niezainicjowany.second
jest przypisywanynull
po zadeklarowaniu.third
parametr jestnull
wartością parametrudefault
Nullable<int>
.null
fourth
jest0
jaknew()
wyrażenie wywołujeNullable<int>
konstruktor iint
jest0
domyślnie.
Język C# 8.0 wprowadził typy odwołań dopuszczające wartości null, w których można wyrazić intencję, że typ odwołania może być null
lub jest zawsze innynull
niż. Możesz myśleć: "Myślałem, że wszystkie typy odwołań są dopuszczane do wartości null!" Nie masz nic złego i są. Ta funkcja umożliwia wyrażenie intencji, którą następnie kompilator próbuje wymusić. Ta sama T?
składnia wyraża, że typ odwołania ma mieć wartość null.
Rozważmy następujący fragment kodu w języku C#:
#nullable enable
string first = string.Empty;
string second;
string? third;
Biorąc pod uwagę powyższy przykład, kompilator wywnioskuje intencję w następujący sposób:
first
nigdy nienull
jest tak, jak na pewno jest przypisany.second
nigdy nie powinna byćnull
wartością , mimo że początkowo jestnull
to . Ocenasecond
przed przypisaniem wartości powoduje wyświetlenie ostrzeżenia kompilatora, ponieważ jest niezainicjowane.third
może to byćnull
. Na przykład może wskazywać elementSystem.String
, ale może wskazywać wartośćnull
. Każda z tych odmian jest akceptowalna. Kompilator pomaga, ostrzegając, jeśli wyłuszczyćthird
bez uprzedniego sprawdzenia, czy nie ma wartości null.
Ważne
Aby można było używać funkcji typów odwołań dopuszczanych do wartości null, jak pokazano powyżej, musi znajdować się w kontekście dopuszczanym do wartości null. Jest to szczegółowo opisane w następnej sekcji.
Kontekst dopuszczany do wartości null
Konteksty dopuszczane do wartości null umożliwiają precyzyjną kontrolę sposobu interpretowania zmiennych typu odwołania przez kompilator. Istnieją cztery możliwe konteksty dopuszczane do wartości null:
disable
: kompilator zachowuje się podobnie do języka C# 7.3 i starszych.enable
: Kompilator włącza całą analizę odwołania o wartości null i wszystkie funkcje języka.warnings
: Kompilator wykonuje całą analizę null i emituje ostrzeżenia, gdy kod może wyłuszczyć wartośćnull
.annotations
: Kompilator nie wykonuje analizy wartości null ani nie emituje ostrzeżeń, gdy kod może wyłuszczyć wyjąteknull
, ale nadal można dodawać adnotacje do kodu przy użyciu typów?
odwołań dopuszczających wartość null i operatorów forgiving null (!
).
Ten moduł ma zakres do disable
kontekstów z możliwością wartości null lub enable
z możliwością wartości null. Aby uzyskać więcej informacji, zapoznaj się z tematem Typy referencyjne dopuszczane do wartości Null: Konteksty dopuszczane do wartości null.
Włączanie typów odwołań dopuszczanych do wartości null
W pliku projektu C# (csproj) dodaj węzeł podrzędny <Nullable>
do <Project>
elementu (lub dołącz do istniejącego <PropertyGroup>
elementu ). Spowoduje to zastosowanie enable
kontekstu dopuszczanego do wartości null do całego projektu.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- Omitted for brevity -->
</Project>
Alternatywnie można ograniczyć zakres kontekstu dopuszczalnego do wartości null do pliku C# przy użyciu dyrektywy kompilatora.
#nullable enable
Poprzednia dyrektywa kompilatora języka C# jest funkcjonalnie równoważna konfiguracji projektu, ale ma zakres do pliku, w którym się znajduje. Aby uzyskać więcej informacji, zobacz Typy referencyjne dopuszczane do wartości null: konteksty dopuszczane do wartości null (docs)
Ważne
Kontekst dopuszczalny do wartości null jest domyślnie włączony w pliku csproj we wszystkich szablonach projektów języka C#, począwszy od platformy .NET 6.0 i nowszych.
Po włączeniu kontekstu dopuszczalnego do wartości null zostaną wyświetlone nowe ostrzeżenia. Rozważmy poprzedni FooBar
przykład, który zawiera dwa ostrzeżenia podczas analizowania w kontekście dopuszczalnym do wartości null:
Wiersz
FooBar fooBar = null;
zawiera ostrzeżenie dotyczącenull
przypisania: Ostrzeżenie C# CS8600: Konwertowanie literału null lub możliwej wartości null na typ inny niż null.Wiersz
_ = fooBar.ToString();
zawiera również ostrzeżenie. Tym razem kompilator obawia się, żefooBar
może mieć wartość null: Ostrzeżenie C# CS8602: Dereference możliwego odwołania o wartości null.
Ważne
Nie ma gwarantowanego bezpieczeństwa o wartości null, nawet jeśli reagujesz i eliminujesz wszystkie ostrzeżenia. Istnieją pewne ograniczone scenariusze, które przejdą analizę kompilatora, ale spowodują utworzenie środowiska uruchomieniowego NullReferenceException
.
Podsumowanie
W tej lekcji przedstawiono sposób włączania kontekstu dopuszczalnego do wartości null w języku C#, aby chronić element przed elementem NullReferenceException
. W następnej lekcji dowiesz się więcej na temat jawnego wyrażania intencji w kontekście dopuszczającym wartość null.