Typy referencyjne dopuszczające wartość null
Typy referencyjne dopuszczające wartość null to zbiór właściwości, które minimalizują prawdopodobieństwo, że kod spowoduje zgłoszenie przez środowisko uruchomieniowe System.NullReferenceException. Trzy funkcje, które pomagają uniknąć tych wyjątków, w tym możliwość jawnego oznaczania typu odwołania jako dopuszczających wartość null:
- Ulepszona analiza przepływu statycznego, która określa, czy zmienna może być
null
przed wyłuszczeniem jej. - Atrybuty, które dodają adnotacje do interfejsów API, aby analiza przepływu określała stan null.
- Adnotacje zmiennych używane przez deweloperów do jawnego deklarowania zamierzonego stanu null dla zmiennej.
Kompilator śledzi stan null każdego wyrażenia w kodzie w czasie kompilacji. Stan zerowy ma jedną z dwóch wartości:
-
not-null: wyrażenie jest znane jako not-
null
. -
może mieć wartość null: wyrażenie może mieć wartość
null
.
Adnotacje zmiennych określają wartość null zmiennej typu odwołania:
-
niepuste: jeśli przypiszesz
null
wartość lub wyrażenie o wartości null do zmiennej, kompilator wyświetla ostrzeżenie. Zmienne, które nie są dopuszczające wartości null, mają domyślny stan null not-null. -
dopuszczać wartość null: możesz przypisać
null
wartość lub wyrażenie o wartości null do zmiennej. Gdy zmienna ma stan null może mieć wartość null, kompilator wyświetla ostrzeżenie w przypadku wyłudzenia zmiennej. Domyślny stan null dla zmiennej to może mieć wartość null.
W pozostałej części tego artykułu opisano, jak działają te trzy obszary funkcjonalne, aby wygenerować ostrzeżenia, gdy kod może dereferencja wartości null
. Wyłuszczenie zmiennej oznacza dostęp do jednego z jej elementów członkowskich przy użyciu .
operatora (kropka), jak pokazano w poniższym przykładzie:
string message = "Hello, World!";
int length = message.Length; // dereferencing "message"
W przypadku wyłudzenia zmiennej, której wartość to null
, środowisko uruchomieniowe zgłasza System.NullReferenceExceptionwartość .
Podobnie ostrzeżenia mogą być generowane, gdy []
notacja jest używana do uzyskiwania dostępu do elementu członkowskiego obiektu, gdy obiekt ma wartość null
:
using System;
public class Collection<T>
{
private T[] array = new T[100];
public T this[int index]
{
get => array[index];
set => array[index] = value;
}
}
public static void Main()
{
Collection<int> c = default;
c[10] = 1; // CS8602: Possible derefence of null
}
Dowiesz się więcej o:
- Analiza stanu null kompilatora: jak kompilator określa, czy wyrażenie nie ma wartości null, czy może ma wartość null.
- Atrybuty stosowane do interfejsów API, które zapewniają więcej kontekstu dla analizy stanu null kompilatora.
- Adnotacje zmiennych dopuszczających wartość null, które zawierają informacje o intencji zmiennych. Adnotacje są przydatne w przypadku pól, parametrów i zwracanych wartości w celu ustawienia domyślnego stanu null.
- Reguły regulujące argumenty typów ogólnych. Dodano nowe ograniczenia, ponieważ parametry typu mogą być typami referencyjnymi lub typami wartości. Sufiks
?
jest implementowany inaczej dla typów wartości dopuszczanych do wartości null i typów odwołań dopuszczanych do wartości null. - Kontekst dopuszczany do wartości null ułatwia migrowanie dużych projektów. Ostrzeżenia i adnotacje można włączyć w kontekście z możliwością null w częściach aplikacji podczas migracji. Po usunięciu większej liczby ostrzeżeń można włączyć oba ustawienia dla całego projektu.
Na koniec poznasz znane pułapki analizy stanu null w struct
typach i tablicach.
Możesz również zapoznać się z tymi pojęciami w naszym module Learn dotyczącym bezpieczeństwa dopuszczanego do wartości null w języku C#.
Analiza stanu null
analiza stanu null śledzi nullowy stan. Wyrażenie nie ma wartości null lub może mieć wartość null. Kompilator określa, że zmienna nie ma wartości null na dwa sposoby:
- Zmienna została przypisana wartość, która jest znana jako nie null.
- Zmienna została sprawdzona względem
null
i nie została przypisana od tamtej pory.
Każda zmienna, której kompilator nie może określić jako nie będące wartością null, jest uważana za może być wartością null. Analiza udostępnia ostrzeżenia w sytuacjach, w których można przypadkowo wyłusić null
wartość. Kompilator generuje ostrzeżenia na podstawie stanu null.
- Gdy zmienna jest nie null, ta zmienna może zostać bezpiecznie wyłuszona.
- Gdy zmienna ma wartość może mieć wartość null, należy ją sprawdzić, aby upewnić się, że nie
null
jest ona przed wyłuszczeniem tej zmiennej.
Rozważmy następujący przykład:
string? message = null;
// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");
var originalMessage = message;
message = "Hello, World!";
// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");
// warning!
Console.WriteLine(originalMessage.Length);
W poprzednim przykładzie kompilator określa, że message
wartość może ma wartość null po wydrukowaniu pierwszego komunikatu. Nie ma ostrzeżenia dla drugiego komunikatu. Ostatni wiersz kodu generuje ostrzeżenie, ponieważ originalMessage
może mieć wartość null. W poniższym przykładzie przedstawiono bardziej praktyczne zastosowanie przechodzenia drzewa węzłów do katalogu głównego, przetwarzanie każdego węzła podczas przechodzenia:
void FindRoot(Node node, Action<Node> processNode)
{
for (var current = node; current != null; current = current.Parent)
{
processNode(current);
}
}
Poprzedni kod nie generuje żadnych ostrzeżeń dotyczących wyłudania zmiennej current
. Analiza statyczna określa, że current
nigdy nie jest wyłuszczany, gdy jest to być może null. Zmienna current
jest sprawdzana null
przed current.Parent
uzyskaniem dostępu i przed przekazaniem current
ProcessNode
do akcji. W poprzednich przykładach pokazano, jak kompilator określa stan null dla zmiennych lokalnych podczas inicjowania, przypisywania lub porównywania z null
.
Analiza stanu null nie śledzi wywoływanych metod. W związku z tym pola zainicjowane w typowej metodzie pomocniczej wywoływanej przez wszystkie konstruktory mogą wygenerować ostrzeżenie z następującym komunikatem:
Właściwość bez wartości null "name" musi zawierać wartość inną niż null podczas zamykania konstruktora.
Te ostrzeżenia można rozwiązać na jeden z dwóch sposobów: łańcuch konstruktora lub atrybuty dopuszczające wartość null w metodzie pomocniczej. Poniższy kod przedstawia przykład każdego z nich. Klasa Person
używa wspólnego konstruktora wywoływanego przez wszystkie inne konstruktory. Klasa Student
ma metodę pomocnika z adnotacjami z atrybutem System.Diagnostics.CodeAnalysis.MemberNotNullAttribute :
using System.Diagnostics.CodeAnalysis;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public Person() : this("John", "Doe") { }
}
public class Student : Person
{
public string Major { get; set; }
public Student(string firstName, string lastName, string major)
: base(firstName, lastName)
{
SetMajor(major);
}
public Student(string firstName, string lastName) :
base(firstName, lastName)
{
SetMajor();
}
public Student()
{
SetMajor();
}
[MemberNotNull(nameof(Major))]
private void SetMajor(string? major = default)
{
Major = major ?? "Undeclared";
}
}
Analiza stanu dopuszczającego wartość null i ostrzeżenia generowane przez kompilator pomagają uniknąć błędów programu przez wyłudzenie null
. Artykuł dotyczący rozpoznawania ostrzeżeń dopuszczających wartość null zawiera techniki poprawiania ostrzeżeń, które najprawdopodobniej są widoczne w kodzie. Diagnostyka utworzona na podstawie analizy stanu null jest tylko ostrzeżeniami.
Atrybuty podpisów interfejsu API
Analiza stanu null wymaga wskazówek od deweloperów, aby zrozumieć semantyka interfejsów API. Niektóre interfejsy API zapewniają kontrole wartości null i powinny zmienić stan null zmiennej z być może-null na not-null. Inne interfejsy API zwracają wyrażenia, które nie mają wartości null lub być może null , w zależności od stanu null argumentów wejściowych. Rozważmy na przykład następujący kod, który wyświetla komunikat w wielkim przypadku:
void PrintMessageUpper(string? message)
{
if (!IsNull(message))
{
Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
}
}
bool IsNull(string? s) => s == null;
Na podstawie inspekcji każdy deweloper rozważy ten kod jako bezpieczny i nie powinien generować ostrzeżeń. Jednak kompilator nie wie, że IsNull
zapewnia sprawdzanie wartości null i wyświetla ostrzeżenie dla message.ToUpper()
instrukcji, biorąc pod uwagę, że message
zmienna może mieć wartość null . Użyj atrybutu , NotNullWhen
aby naprawić to ostrzeżenie:
bool IsNull([NotNullWhen(false)] string? s) => s == null;
Ten atrybut informuje kompilator, że jeśli IsNull
zwraca false
wartość , parametr s
nie ma wartości null. Kompilator zmienia stanmessage
null na wartość not-null wewnątrz if (!IsNull(message)) {...}
bloku. Nie są wyświetlane żadne ostrzeżenia.
Atrybuty zawierają szczegółowe informacje o stanie null argumentów, zwracanych wartościach i elementach członkowskich wystąpienia obiektu używanego do wywoływania elementu członkowskiego. Szczegółowe informacje na temat każdego atrybutu można znaleźć w artykule referencyjnym języka na temat atrybutów referencyjnych dopuszczanych do wartości null. Od platformy .NET 5 wszystkie interfejsy API środowiska uruchomieniowego platformy .NET są oznaczone adnotacjami. Możesz ulepszyć analizę statyczną przez dodawanie adnotacji do interfejsów API w celu udostępnienia semantycznych informacji o stanie null argumentów i zwracanych wartościach.
Adnotacje zmiennych dopuszczanych do wartości null
Analiza stanu null zapewnia niezawodną analizę zmiennych lokalnych. Kompilator potrzebuje więcej informacji na temat zmiennych członkowskich. Kompilator potrzebuje więcej informacji, aby ustawić stan null wszystkich pól w nawiasie otwierającym elementu członkowskiego. Dowolny z dostępnych konstruktorów może służyć do inicjowania obiektu. Jeśli pole elementu członkowskiego może kiedykolwiek zostać ustawione na null
wartość , kompilator musi przyjąć, że jego stan null ma wartość może mieć wartość null na początku każdej metody.
Używasz adnotacji, które mogą deklarować, czy zmienna jest typem odwołania dopuszczanym do wartości null, czy typem referencyjnym bez wartości null. Te adnotacje składają ważne instrukcje dotyczące stanu null dla zmiennych:
-
Odwołanie nie powinno mieć wartości null. Domyślny stan zmiennej referencyjnej bez wartości null nie ma wartości null. Kompilator wymusza reguły, które zapewniają, że bezpieczne jest wyłudzenie tych zmiennych bez uprzedniego sprawdzenia, czy nie ma wartości null:
- Zmienna musi zostać zainicjowana do wartości innej niż null.
- Zmienna nigdy nie może mieć przypisanej wartości
null
. Kompilator generuje ostrzeżenie, gdy kod przypisuje wyrażenie może o wartości null do zmiennej, która nie powinna mieć wartości null.
-
Odwołanie może mieć wartość null. Domyślny stan zmiennej referencyjnej dopuszczanej do wartości null to może mieć wartość null. Kompilator wymusza reguły, aby upewnić się, że prawidłowo sprawdzasz, czy nie ma odwołania
null
:- Zmienna może zostać wyłuszona tylko wtedy, gdy kompilator może zagwarantować, że wartość nie jest
null
. - Te zmienne można zainicjować przy użyciu wartości domyślnej
null
i można przypisać wartośćnull
w innym kodzie. - Kompilator nie generuje ostrzeżeń, gdy kod przypisuje wyrażenie może mieć wartość null do zmiennej, która może mieć wartość null.
- Zmienna może zostać wyłuszona tylko wtedy, gdy kompilator może zagwarantować, że wartość nie jest
Każda zmienna referencyjna, która nie może być null, ma początkowy stan nie null. Każda zmienna referencyjna dopuszczana do wartości null ma początkowy stan null może mieć wartość null.
Typ odwołania dopuszczalnego do wartości null jest zanotowany przy użyciu tej samej składni co typy wartości dopuszczającej wartość null: element ?
jest dołączany do typu zmiennej. Na przykład następująca deklaracja zmiennej reprezentuje zmienną ciągu dopuszczaną do wartości null, name
:
string? name;
Po włączeniu typów odwołań dopuszczanych do wartości null każda zmienna, w której ?
nie jest dołączana do nazwy typu, jest typem referencyjnym bez wartości null. Obejmuje to wszystkie zmienne typu odwołania w istniejącym kodzie po włączeniu tej funkcji. Jednak wszelkie niejawnie wpisane zmienne lokalne (zadeklarowane przy użyciu var
) są typami referencyjnymi dopuszczanymi wartości null. Jak pokazano w poprzednich sekcjach, analiza statyczna określa stan null zmiennych lokalnych, aby określić, czy są one być może null przed wyłuszczeniem go.
Czasami należy zastąpić ostrzeżenie, gdy wiadomo, że zmienna nie ma wartości null, ale kompilator określa, że jego stan null jest być może null. Operator forgiving o wartości null jest używany po nazwie zmiennej, aby wymusić, że stan null ma wartość !
. Jeśli na przykład wiesz, że zmienna name
nie null
jest, ale kompilator generuje ostrzeżenie, możesz napisać następujący kod, aby zastąpić analizę kompilatora:
name!.Length;
Typy referencyjne dopuszczające wartość null i typy wartości dopuszczających wartość null zapewniają podobną koncepcję semantyczną: Zmienna może reprezentować wartość lub obiekt albo zmienną może być null
. Jednak typy referencyjne dopuszczane do wartości null i typy wartości dopuszczanych do wartości null są implementowane inaczej: typy wartości dopuszczalnych wartości null są implementowane przy użyciu System.Nullable<T>, a typy referencyjne dopuszczane do wartości null są implementowane przez atrybuty odczytywane przez kompilator. Na przykład string?
i string
są reprezentowane przez ten sam typ: System.String.
int?
Jednak i int
są reprezentowane odpowiednio przez System.Nullable<System.Int32>
i System.Int32.
Typy referencyjne dopuszczane do wartości null to funkcja czasu kompilacji. Oznacza to, że osoby wywołujące mogą ignorować ostrzeżenia, celowo użyć null
jako argumentu do metody, która oczekuje odwołania innego niż null. Autorzy bibliotek powinni uwzględnić kontrole czasu wykonywania względem wartości argumentów o wartościach null. Jest ArgumentNullException.ThrowIfNull to preferowana opcja sprawdzania parametru względem wartości null w czasie wykonywania. Ponadto zachowanie środowiska uruchomieniowego programu używającego adnotacji 'nullable' jest takie samo, jeśli wszystkie adnotacje 'nullable' (?
i !
) zostaną usunięte. Jedyne ich przeznaczenie to wyrażanie intencji projektu i dostarczanie informacji na potrzeby analizy stanu zerowego.
Ważne
Włączenie adnotacji dopuszczających wartość null może zmienić sposób określania sposobu, w jaki platforma Entity Framework Core określa, czy element członkowski danych jest wymagany. Więcej szczegółów można znaleźć w artykule Dotyczącym podstaw platformy Entity Framework Core: praca z typami referencyjnymi dopuszczanymi wartościami null.
Typy ogólne
Typy ogólne wymagają szczegółowych reguł do obsługi T?
dla dowolnego parametru T
typu . Reguły są koniecznie szczegółowe ze względu na historię i inną implementację typu wartości dopuszczanej do wartości null i typ odwołania dopuszczalnego wartości null.
Typy wartości dopuszczalnych wartości są implementowane przy użyciu System.Nullable<T> struktury .
Typy referencyjne dopuszczające wartość null są implementowane jako adnotacje typu, które zapewniają semantyczne reguły kompilatora.
- Jeśli argument typu dla
T
jest typem odwołania,T?
odwołuje się do odpowiedniego typu odwołania dopuszczanego do wartości null. Jeśli na przykładT
element tostring
,T?
jest tostring?
. - Jeśli argument typu dla
T
jest typem wartości,T?
odwołuje się do tego samego typu wartości,T
. Jeśli na przykładT
element toint
,T?
element jest również .int
- Jeśli argument typu dla
T
elementu to typ odwołania dopuszczający wartość null,T?
odwołuje się do tego samego typu odwołania dopuszczanego do wartości null. Na przykład jeśliT
element tostring?
,T?
jest również wartościąstring?
. - Jeśli argument typu dla
T
jest typem wartości dopuszczanej do wartości null,T?
odwołuje się do tego samego typu wartości dopuszczających wartość null. Na przykład jeśliT
element toint?
,T?
jest również wartościąint?
.
W przypadku wartości T?
zwracanych jest równoważne wartościom [MaybeNull]T
; dla wartości T?
argumentów jest równoważne .[AllowNull]T
Aby uzyskać więcej informacji, zobacz artykuł Atrybuty analizy stanu null w dokumentacji języka.
Możesz określić różne zachowanie przy użyciu ograniczeń:
- Ograniczenie
class
oznacza, żeT
musi być niepustym typem odwołania (na przykładstring
). Kompilator generuje ostrzeżenie, jeśli używasz typu odwołania dopuszczanego do wartości null, takiego jakstring?
dla elementuT
. - Ograniczenie
class?
oznacza, żeT
musi być typem odwołania, innym niż null () lub typem odwołania dopuszczanym do wartości null (string
na przykładstring?
). Gdy parametr typu jest typem odwołania dopuszczanym do wartości null, takim jakstring?
, wyrażenieT?
odwołań, które to samo typu odwołania dopuszczające wartość null, takie jakstring?
. - Ograniczenie
notnull
oznacza, żeT
musi być typem referencyjnym bez wartości null lub typem wartości innej niż null. Jeśli używasz typu odwołania dopuszczanego do wartości null lub typu wartości dopuszczanej do wartości null dla parametru typu, kompilator generuje ostrzeżenie. Ponadto, gdyT
jest typem wartości, zwracana wartość jest typem wartości, a nie odpowiadającym mu typem wartości null.
Te ograniczenia pomagają dostarczyć więcej informacji do kompilatora na temat sposobu T
użycia. Pomaga to, gdy deweloperzy wybierają typ i T
zapewniają lepszą analizę stanu null, gdy jest używane wystąpienie typu ogólnego.
Kontekst dopuszczany do wartości null
Kontekst dopuszczający wartość null określa, jak obsługiwane są adnotacje typu odwołania dopuszczające wartość null oraz jakie ostrzeżenia są generowane przez statyczną analizę stanu null. Kontekst dopuszczający wartość null zawiera dwie flagi: ustawienie adnotacji oraz ostrzeżenia .
Zarówno ustawienia adnotacji , jak i ustawienia ostrzeżeń są domyślnie wyłączone dla istniejących projektów. Począwszy od platformy .NET 6 (C# 10), obie flagi są domyślnie włączone dla nowych projektów. Powodem istnienia dwóch odrębnych flag dla kontekstu dopuszczającego wartość null jest ułatwienie migracji dużych projektów, które powstały przed wprowadzeniem typów odniesienia dopuszczających wartość null.
W przypadku małych projektów można włączyć typy odwołań dopuszczające wartości null, naprawić ostrzeżenia i kontynuować. Jednak w przypadku większych projektów i rozwiązań wieloprojektowych może to spowodować wygenerowanie dużej liczby ostrzeżeń. Możesz użyć pragmas, aby włączyć typy odwołań dopuszczane do wartości null file-by-file podczas rozpoczynania korzystania z typów odwołań dopuszczanych do wartości null. Nowe funkcje chroniące przed zgłaszaniem System.NullReferenceException elementu mogą być destrukcyjne po włączeniu w istniejącej bazie kodu:
- Wszystkie jawnie wpisane zmienne referencyjne są interpretowane jako typy referencyjne, które nie mogą być dopuszczane do wartości null.
- Znaczenie
class
ograniczenia w rodzajach ogólnych zmieniło się na oznaczające typ odwołania niezwiązany z wartością null. - Nowe ostrzeżenia są generowane z powodu tych nowych reguł.
Kontekst adnotacji dopuszczania wartości null określa zachowanie kompilatora. Istnieją cztery kombinacje ustawień kontekstu dopuszczających wartości null dla .
-
obie wyłączone: kod jest ignorujący wartości null.
Wyłącz pasuje do zachowania przed włączeniem typów odwołań dopuszczanych do wartości null, z wyjątkiem nowej składni powoduje wygenerowanie ostrzeżeń zamiast błędów.
- Ostrzeżenia dopuszczające wartość null są wyłączone.
- Wszystkie zmienne typu odwołania są typami referencyjnymi dopuszczanymi wartościami null.
- Użyj sufiksu
?
, aby zadeklarować typ odwołania dopuszczający wartość null, powoduje wygenerowanie ostrzeżenia. - Możesz użyć operatora forgiving o wartości null ,
!
ale nie ma żadnego efektu.
-
oba włączone: kompilator włącza wszystkie analizy odwołań wartości null i wszystkie funkcje językowe.
- Wszystkie nowe ostrzeżenia dopuszczające wartość null są włączone.
- Można użyć sufiksu
?
, aby zadeklarować typ odwołania dopuszczanego do wartości null. - Zmienne typu odwołania bez sufiksu
?
są typami referencyjnymi bez wartości null. - Operator forgiving o wartości null pomija ostrzeżenia dotyczące możliwego wyłudzenia wartości
null
.
-
ostrzeżenie włączone: kompilator dokonuje analizy wartości null i emituje ostrzeżenia, gdy kod może odwołać się do
null
.- Wszystkie nowe ostrzeżenia dopuszczające wartość null są włączone.
- Użyj sufiksu
?
, aby zadeklarować typ odwołania dopuszczający wartość null, powoduje wygenerowanie ostrzeżenia. - Wszystkie zmienne typu odwołania mogą mieć wartość null. Jednak składowe mają stannull not-null dla otwierającego nawiasu klamrowego wszystkich metod, chyba że zadeklarowane z sufiksem
?
. - Możesz użyć operatora forgiving o wartości null,
!
.
-
włączone adnotacje: kompilator nie wyświetla ostrzeżeń, gdy kod może odwołać się do
null
, lub podczas przypisywania wyrażenia, które może mieć wartość null, do zmiennej nienullowej.- Wszystkie nowe ostrzeżenia dopuszczające wartość null są wyłączone.
- Można użyć sufiksu
?
, aby zadeklarować typ odwołania dopuszczanego do wartości null. - Zmienne typu odwołania bez sufiksu
?
są typami referencyjnymi bez wartości null. - Możesz użyć operatora forgiving o wartości null ,
!
ale nie ma żadnego efektu.
Kontekst adnotacji dopuszczający wartość null i kontekst ostrzeżenia dopuszczający wartość null można ustawić dla projektu przy użyciu <Nullable>
elementu w pliku csproj . Ten element konfiguruje sposób, w jaki kompilator interpretuje wartość null typów i jakie ostrzeżenia są emitowane. W poniższej tabeli przedstawiono dozwolone wartości i podsumowano określone konteksty.
Kontekst | Ostrzeżenia dotyczące wyłudzenia | Ostrzeżenia dotyczące przypisania | Typy odwołań |
? przyrostek |
! operator |
---|---|---|---|---|---|
disable |
Disabled | Disabled | Wszystkie są dopuszczane do wartości null | Tworzy ostrzeżenie | Nie ma żadnego efektu |
enable |
Włączony | Włączony | Bez wartości null, chyba że zadeklarowane za pomocą polecenia ? |
Deklaruje typ dopuszczania wartości null | Pomija ostrzeżenia dotyczące możliwego null przypisania |
warnings |
Włączony | Nie dotyczy | Wszystkie są dopuszczane do wartości null, ale elementy członkowskie są uznawane za niepuste podczas otwierania nawiasu klamrowego metod | Tworzy ostrzeżenie | Pomija ostrzeżenia dotyczące możliwego null przypisania |
annotations |
Disabled | Disabled | Bez wartości null, chyba że zadeklarowane za pomocą polecenia ? |
Deklaruje typ dopuszczania wartości null | Nie ma żadnego efektu |
Zmienne typu odwołania w kodzie skompilowanym w kontekście wyłączonym są nieświadome wartości null. Można przypisać literał lub zmienną null
może mieć wartość null do zmiennej, która jest niepamiętna. Jednak domyślny stan zmiennej o wartości null nie jest zerowy.
Możesz wybrać, które ustawienie jest najlepsze dla projektu:
- Wybierz opcję wyłącz dla starszych projektów, których nie chcesz aktualizować na podstawie diagnostyki ani nowych funkcji.
- Wybierz ostrzeżenia, aby określić, gdzie może zostać zgłoszony System.NullReferenceExceptionkod. Te ostrzeżenia można rozwiązać przed zmodyfikowaniem kodu w celu włączenia typów odwołań niezwiązanych z wartościami null.
- Wybierz adnotacje , aby wyrazić intencję projektu przed włączeniem ostrzeżeń.
- Wybierz opcję Włącz dla nowych projektów i aktywnych projektów, w których chcesz chronić przed wyjątkami odwołania o wartości null.
Przykład:
<Nullable>enable</Nullable>
Możesz również użyć dyrektyw, aby ustawić te same flagi w dowolnym miejscu w kodzie źródłowym. Te dyrektywy są najbardziej przydatne podczas migrowania dużej bazy kodu.
-
#nullable enable
: ustawia flagi adnotacji i ostrzeżeń, aby włączyć. -
#nullable disable
: ustawia flagi adnotacji i ostrzeżeń, aby wyłączyć. -
#nullable restore
: przywraca flagę adnotacji i flagę ostrzeżenia do ustawień projektu. -
#nullable disable warnings
: ustaw flagę ostrzeżenia, aby wyłączyć. -
#nullable enable warnings
: Ustaw flagę ostrzeżenia na , aby włączyć. -
#nullable restore warnings
: przywraca flagę ostrzeżenia do ustawień projektu. -
#nullable disable annotations
: ustaw flagę adnotacji, aby wyłączyć. -
#nullable enable annotations
: ustaw flagę adnotacji, aby włączyć. -
#nullable restore annotations
: przywraca flagę adnotacji do ustawień projektu.
W przypadku dowolnego wiersza kodu można ustawić dowolną z następujących kombinacji:
Flaga ostrzeżenia | Flaga adnotacji | Używanie |
---|---|---|
domyślna wartość projektu | domyślna wartość projektu | Wartość domyślna |
Włącz | Wyłącz | Naprawianie ostrzeżeń dotyczących analizy |
Włącz | domyślna wartość projektu | Naprawianie ostrzeżeń dotyczących analizy |
domyślna wartość projektu | Włącz | Dodawanie adnotacji typu |
Włącz | Włącz | Kod został już zmigrowany |
Wyłącz | Włącz | Dodawanie adnotacji do kodu przed naprawieniem ostrzeżeń |
Wyłącz | Wyłącz | Dodawanie starszego kodu do zmigrowanego projektu |
domyślna wartość projektu | Wyłącz | Rzadko |
Wyłącz | domyślna wartość projektu | Rzadko |
Te dziewięć kombinacji zapewnia szczegółową kontrolę nad diagnostyką emitowaną przez kompilator kodu. Możesz włączyć więcej funkcji w dowolnym obszarze, który aktualizujesz, bez wyświetlania większej liczby ostrzeżeń, które nie są jeszcze gotowe do rozwiązania.
Ważne
Globalny kontekst dopuszczania wartości null nie ma zastosowania do wygenerowanych plików kodu. W ramach każdej strategii kontekst dopuszczalny do wartości null jest wyłączony dla dowolnego pliku źródłowego oznaczonego jako wygenerowany. Oznacza to, że wszystkie interfejsy API w wygenerowanych plikach nie są adnotacjami. Nie są generowane żadne ostrzeżenia o wartości null dla wygenerowanych plików. Istnieją cztery sposoby oznaczania pliku jako wygenerowanego:
- W pliku .editorconfig określ
generated_code = true
w sekcji, która ma zastosowanie do tego pliku. - Umieść
<auto-generated>
lub<auto-generated/>
w komentarzu w górnej części pliku. Może on znajdować się w dowolnym wierszu w tym komentarzu, ale blok komentarza musi być pierwszym elementem w pliku. - Uruchom nazwę pliku przy użyciu TemporaryGeneratedFile_
- Zakończ nazwę pliku .designer.cs, .generated.cs, .g.cs lub .g.i.cs.
Generatory mogą korzystać z #nullable
dyrektywy preprocesora.
Domyślnie flagi adnotacji dopuszczające wartość null i ostrzeżenia są wyłączone. Oznacza to, że istniejący kod kompiluje się bez zmian i bez generowania nowych ostrzeżeń. Począwszy od platformy .NET 6, nowe projekty obejmują element <Nullable>enable</Nullable>
we wszystkich szablonach projektów, ustawiając te flagi na włączone.
Te opcje zapewniają dwie odrębne strategie aktualizacji istniejącej bazy kodu w celu używania typów odwołań dopuszczanych do wartości null.
Znane pułapki
Tablice i struktury zawierające typy referencyjne są znanymi pułapkami w odwołaniach dopuszczających wartość null i analizą statyczną, która określa bezpieczeństwo wartości null. W obu sytuacjach odwołanie nie dopuszczające wartości null może zostać zainicjowane na null
, bez generowania ostrzeżeń.
Struktury
Struktura, która zawiera typy referencyjne niezwiązane z wartościami null, umożliwia przypisywanie default
jej bez żadnych ostrzeżeń. Rozważmy następujący przykład:
using System;
#nullable enable
public struct Student
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
public static class Program
{
public static void PrintStudent(Student student)
{
Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
}
public static void Main() => PrintStudent(default);
}
W poprzednim przykładzie nie ma żadnego ostrzeżenia PrintStudent(default)
, podczas gdy typy FirstName
odwołań bez wartości null i LastName
mają wartość null.
Innym częstszym przypadkiem jest sytuacja, w której zajmujesz się strukturami ogólnymi. Rozważmy następujący przykład:
#nullable enable
public struct S<T>
{
public T Prop { get; set; }
}
public static class Program
{
public static void Main()
{
string s = default(S<string>).Prop;
}
}
W poprzednim przykładzie właściwość Prop
jest null
w czasie wykonywania. Jest on przypisany do ciągu bez wartości null bez żadnych ostrzeżeń.
Tablice
Tablice są również znanymi pułapkami w typach odwołań dopuszczanych do wartości null. Rozważmy następujący przykład, który nie generuje żadnych ostrzeżeń:
using System;
#nullable enable
public static class Program
{
public static void Main()
{
string[] values = new string[10];
string s = values[0];
Console.WriteLine(s.ToUpper());
}
}
W poprzednim przykładzie deklaracja tablicy pokazuje, że zawiera ciągi niepuste, podczas gdy jego elementy są inicjowane na null
wartość . Następnie zmienna s
ma przypisaną null
wartość (pierwszy element tablicy). Na koniec zmienna s
jest wyłuszczana, powodując wyjątek środowiska uruchomieniowego.