Typy referencyjne dopuszczające wartość null

W kontekście niepamiętnym dopuszczanym do wartości null wszystkie typy odwołań były dopuszczane do wartości null. Typy referencyjne dopuszczające wartość null odnoszą się do grupy funkcji włączonej w kontekście obsługującym wartość null, które minimalizują prawdopodobieństwo, że kod powoduje, że środowisko uruchomieniowe zgłasza błąd System.NullReferenceException. Typy odwołań dopuszczające wartość null obejmują trzy funkcje, które pomagają uniknąć tych wyjątków, w tym możliwość jawnego oznaczania typu odwołania jako dopuszczanego do wartości 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 null ma jedną z trzech wartości:

  • not-null: wyrażenie jest znane jako not-null.
  • może mieć wartość null: wyrażenie może mieć wartość null.
  • nieświadomy: kompilator nie może określić stanu null wyrażenia.

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.
  • nieświadomy: możesz przypisać null wartość lub wyrażenie o wartości null do zmiennej. Kompilator nie wyświetla ostrzeżeń podczas wyłudzenia zmiennej lub podczas przypisywania wyrażenia może o wartości null do zmiennej.

Niepamiętny stan null i nieświadoma wartość null są zgodne z zachowaniem przed wprowadzeniem typów odwołań dopuszczanych do wartości null. Te wartości są przydatne podczas migracji lub gdy aplikacja używa biblioteki, która nie włączyła typów odwołań dopuszczających wartości null.

Analiza stanu null i adnotacje zmiennych są domyślnie wyłączone dla istniejących projektów — co oznacza, że wszystkie typy odwołań nadal mogą mieć wartość null. Począwszy od platformy .NET 6, są one domyślnie włączone dla nowych projektów. Aby uzyskać informacje na temat włączania tych funkcji przez deklarowanie kontekstu adnotacji dopuszczającego wartość null, zobacz Konteksty dopuszczające wartość null.

W pozostałej części tego artykułu opisano sposób działania tych trzech obszarów funkcji w celu wygenerowania ostrzeżeń, gdy kod może wyłuszczyć null wartość. 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ść .

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 do ustawiania domyślnego stanu null na początku metod składowych.
  • 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.
  • Konteksty dopuszczane do wartości null ułatwiają migrowanie dużych projektów. Podczas migracji można włączyć konteksty dopuszczające wartość null lub ostrzeżenia w częściach aplikacji. Po wystąpieniu większej liczby ostrzeżeń można włączyć typy odwołań dopuszczające wartość null 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

Po włączeniu typów odwołań dopuszczanych wartości null analiza stanu null śledzi stan null odwołań. 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:

  1. Do zmiennej przypisano wartość, która jest znana jako not-null.
  2. Zmienna została sprawdzona względem null elementu i nie została zmodyfikowana od tego sprawdzenia.

Jeśli typy odwołań dopuszczane do wartości null nie są włączone, wszystkie wyrażenia mają stan null nieświadomy. W pozostałej części sekcji opisano zachowanie, gdy są włączone typy odwołań dopuszczane do wartości null.

Każda zmienna, która nie została określona przez kompilator jako not-null , jest uznawana za wartość może-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.

  • Jeśli zmienna nie ma wartości 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 currentProcessNode 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 generują ostrzeżenie z następującym szablonem:

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";
    }
}

Uwaga

W języku C# 10 dodano szereg ulepszeń dotyczących określonego przypisania i analizy stanu null. Podczas uaktualniania do wersji C# 10 może być mniej ostrzeżeń dopuszczających wartość null, które są fałszywie dodatnie. Więcej informacji na temat ulepszeń specyfikacji funkcji dotyczących określonych ulepszeń przypisania można dowiedzieć się więcej.

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.

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 falsewartość , 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 nullwartość , 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 nulljest wartością .
    • Te zmienne mogą być inicjowane z wartością domyślną null i mogą być przypisane do wartości 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.

Każda zmienna referencyjna bez wartości null ma domyślny stannull not-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ść not-null.! 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.

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 Ttypu . 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ład T element to string, T? jest to string?.
  • 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ład T element to int, 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śli T element to string?, 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śli T element to int?, 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, że T musi być niepustym typem odwołania (na przykład string). Kompilator generuje ostrzeżenie, jeśli używasz typu odwołania dopuszczanego do wartości null, takiego jak string? dla elementu T.
  • Ograniczenie class? oznacza, że T musi być typem odwołania, innym niż null () lub typem odwołania dopuszczanym do wartości null (stringna przykład string?). Gdy parametr typu jest typem odwołania dopuszczanym do wartości null, takim jak string?, wyrażenie T? odwołań, które to samo typu odwołania dopuszczające wartość null, takie jak string?.
  • Ograniczenie notnull oznacza, że T 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, gdy T 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.

Konteksty dopuszczane do wartości 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 wartości kontekstu adnotacji dopuszczanego do wartości null:

  • disable: kod ma wartość null- nieświadomy. 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.
  • enable: kompilator włącza wszystkie analizy odwołań o wartości null i wszystkie funkcje języka.
    • 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 przypisania do nullelementu .
  • ostrzeżenia: kompilator wykonuje całą analizę o wartości null i emituje ostrzeżenia, gdy kod może wyłuszczyć wyjątek 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, !.
  • adnotacje: kompilator nie emituje ostrzeżeń, gdy kod może wyłuszczyć nullwartość , lub podczas przypisywania wyrażenia może mieć wartość null do zmiennej niepustej.
    • 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ń ? Sufiks ! 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łączona 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 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 konteksty w dowolnym miejscu w kodzie źródłowym. Te dyrektywy są najbardziej przydatne podczas migrowania dużej bazy kodu.

  • #nullable enable: ustawia kontekst adnotacji dopuszczający wartość null i kontekst ostrzeżenia dopuszczający wartość null, aby włączyć.
  • #nullable disable: ustawia kontekst adnotacji dopuszczający wartość null i kontekst ostrzeżenia dopuszczający wartość null, aby wyłączyć.
  • #nullable restore: przywraca kontekst adnotacji dopuszczający wartość null i kontekst ostrzeżenia dopuszczający wartość null do ustawień projektu.
  • #nullable disable warnings: Ustaw kontekst ostrzeżenia dopuszczający wartość null, aby wyłączyć.
  • #nullable enable warnings: Ustaw kontekst ostrzeżenia dopuszczający wartość null, aby włączyć.
  • #nullable restore warnings: przywraca kontekst ostrzeżenia dopuszczający wartość null do ustawień projektu.
  • #nullable disable annotations: Ustaw kontekst adnotacji dopuszczalnej wartości null, aby wyłączyć.
  • #nullable enable annotations: Ustaw kontekst adnotacji dopuszczalnej wartości null, aby włączyć.
  • #nullable restore annotations: przywraca kontekst ostrzeżenia adnotacji do ustawień projektu.

W przypadku dowolnego wiersza kodu można ustawić dowolną z następujących kombinacji:

Kontekst ostrzeżenia Kontekst 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. Istnieją cztery sposoby oznaczania pliku jako wygenerowanego:

  1. W pliku .editorconfig określ generated_code = true w sekcji, która ma zastosowanie do tego pliku.
  2. 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.
  3. Uruchom nazwę pliku przy użyciu TemporaryGeneratedFile_
  4. Zakończ nazwę pliku .designer.cs, .generated.cs, .g.cs lub .g.i.cs.

Generatory mogą korzystać z #nullable dyrektywy preprocesora.

Domyślnie konteksty 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 zawierają <Nullable>enable</Nullable> element we wszystkich szablonach projektów.

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 nullwartość . Następnie zmienna s ma przypisaną null wartość (pierwszy element tablicy). Na koniec zmienna s jest wyłuszczana, powodując wyjątek środowiska uruchomieniowego.

Zobacz też