Číst v angličtině

Sdílet prostřednictvím


Odkazové typy s možnou hodnotou null

Nulovatelné odkazové typy jsou skupina vlastností, které minimalizují pravděpodobnost, že váš kód způsobí, že běhové prostředí vyvolá System.NullReferenceException. Tři funkce, které vám pomohou vyhnout se těmto výjimkám, včetně možnosti explicitně označit typ odkazu jako nullable:

  • Vylepšená statická analýza toku, která určuje, jestli může být null proměnná před jeho odvozováním.
  • Atributy, které anotují rozhraní API tak, aby analýza toku určila stav nulový.
  • Poznámky k proměnným, které vývojáři používají k explicitní deklaraci zamýšleného stavu null pro proměnnou.

Kompilátor sleduje stav null každého výrazu v kódu v době kompilace. Stav null má jednu ze dvou hodnot:

  • not-null: Je známo, že výraz není null.
  • možná-null: Výraz může být null.

Poznámky k proměnným určují nulovou hodnotu proměnné typu odkazu:

  • non-nullable: Pokud proměnné přiřadíte null hodnotu nebo výraz, který může být null, kompilátor vás upozorní. Proměnné, které jsou non-nullable, mají výchozí stav not-null.
  • nullable: Proměnné můžete přiřadit hodnotu null nebo výraz, který může být null. Pokud je stav proměnné možná-null, kompilátor vydá upozornění, pokud proměnnou dereferencujete. Výchozí stav null pro proměnnou je možná null.

Zbytek tohoto článku popisuje, jak tyto tři oblasti funkcí fungují, aby vytvářely upozornění, když váš kód může dereferencovat hodnotu null. Dereferencing proměnné znamená přístup k jednomu z jejích členů pomocí operátoru . (tečka), jak je znázorněno v následujícím příkladu:

C#
string message = "Hello, World!";
int length = message.Length; // dereferencing "message"

Při dereferencování proměnné, jejíž hodnota je null, modul runtime vygeneruje System.NullReferenceException výjimku.

Podobně lze upozornění vytvořit, když je zápis [] použit pro přístup k členu objektu, když je objekt null.

C#
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 dereference of null
}

Dozvíte se o:

  • Analýza nullových stavů kompilátoru: jak kompilátor určuje, zda výraz není null, nebo může být null.
  • Atributy, které se aplikují na rozhraní API a poskytují více kontextu pro analýzu null-stavu kompilátoru.
  • Poznámky pro proměnné s možností null, které poskytují informace o vašem záměru pro proměnné. Poznámky jsou užitečné pro pole, parametry a návratové hodnoty k nastavení výchozího stavu null.
  • Pravidla, která řídí argumenty obecného typu. Byla přidána nová omezení, protože parametry typu mohou být odkazové typy nebo typy hodnot. Přípona ? je implementována odlišně pro typy hodnot s možnou hodnotou null a odkazové typy s možnou hodnotou null.
  • Kontext Nullable vám pomůže migrovat rozsáhlé projekty. Upozornění a poznámky můžete povolit v kontextu s možnou hodnotou null v částech aplikace při migraci. Po vyřešení dalších upozornění můžete povolit obě nastavení pro celý projekt.

Nakonec zjistíte známé nástrahy analýzy stavu null v struct typech a polích.

Tyto koncepty můžete prozkoumat také v našem modulu Learn o bezpečnosti s možnou hodnotou Null v jazyce C#.

Analýza nulového stavu

Analýza nulového stavu sleduje nulový stav referencí. Výraz buď není null, nebo možná null. Kompilátor určuje, že proměnná není null dvěma způsoby:

  1. Proměnné byla přiřazena hodnota, o které je známo, že není nulová .
  2. Proměnná byla zkontrolována proti null a od té doby nebyla přiřazena.

Každá proměnná, kterou kompilátor nemůže určit jako nenulová, se považuje za možná nulová. Analýza poskytuje upozornění v situacích, kdy byste mohli omylem dereferencovat hodnotu null. Kompilátor generuje upozornění na základě null-stavu.

  • Pokud proměnná není null, může být tato proměnná bezpečně dereferencována.
  • Pokud je proměnná může být null, musí být zkontrolována, aby se zajistilo, že není null před dereferencováním.

Představte si následující příklad:

C#
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);

V předchozím příkladu kompilátor určuje, že message je možná null při tisku první zprávy. U druhé zprávy není žádné upozornění. Poslední řádek kódu vytvoří upozornění, protože originalMessage může mít hodnotu null. Následující příklad ukazuje praktičtější použití při procházení stromu uzlů ke kořeni stromu a zpracování každého uzlu během procházení:

C#
void FindRoot(Node node, Action<Node> processNode)
{
    for (var current = node; current != null; current = current.Parent)
    {
        processNode(current);
    }
}

Předchozí kód nevygeneruje žádná upozornění pro dereferencování proměnné current. Statická analýza určuje, že current není nikdy dereferencován, pokud může být null. Proměnná current se porovnává s null před přístupem k current.Parent, a než se předá current akci ProcessNode. Předchozí příklady ukazují, jak kompilátor určuje stav null pro místní proměnné při inicializaci, přiřazení nebo porovnání s null.

Analýza stavu null neprovádí trasování do volaných metod. V důsledku toho můžou pole inicializovaná v běžné pomocné metodě volané všemi konstruktory generovat upozornění s následující zprávou:

Při ukončení konstruktoru musí vlastnost name obsahovat nenulovou hodnotu.

Tato upozornění můžete řešit jedním ze dvou způsobů: řetězení konstruktoru nebo atributy s možnou hodnotou null v pomocné metodě. Následující kód ukazuje příklad každého z nich. Třída Person používá společný konstruktor volaný všemi ostatními konstruktory. Třída Student má pomocnou metodu anotovanou atributem System.Diagnostics.CodeAnalysis.MemberNotNullAttribute :

C#

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

Analýza stavu s možnou hodnotou null a upozornění, která kompilátor generuje, vám pomůže vyhnout se chybám programu dereferencováním null. Článek o řešení upozorněními pro nullable typy poskytuje techniky pro opravu upozornění, která pravděpodobně vidíte ve svém kódu. Diagnostika vytvořená z analýzy stavu null jsou pouze upozornění.

Atributy podpisů rozhraní API

Analýza stavu null potřebuje od vývojářů rady, aby porozuměli sémantice rozhraní API. Některá rozhraní API poskytují kontroly hodnot null a mění stav null proměnné z možná null na není null. Jiná rozhraní API vrací výrazy, které nejsou null nebo možná null v závislosti na stavu null vstupních argumentů. Představte si například následující kód, který zobrazí zprávu velkými písmeny:

C#
void PrintMessageUpper(string? message)
{
    if (!IsNull(message))
    {
        Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
    }
}

bool IsNull(string? s) => s == null;

Na základě kontroly by každý vývojář považoval tento kód za bezpečný a neměl by generovat upozornění. Kompilátor však neví, že IsNull poskytuje kontrolu hodnoty null a vydává upozornění pro message.ToUpper() příkaz, přičemž zvažuje message , že se má jednat o proměnnou s možnou hodnotou null . Toto upozornění opravíte pomocí atributu NotNullWhen :

C#
bool IsNull([NotNullWhen(false)] string? s) => s == null;

Tento atribut informuje kompilátor, že pokud IsNull vrátí false, parametr s není null. Kompilátor změní null-stavmessage na not-null uvnitř if (!IsNull(message)) {...} bloku. Nejsou vydána žádná upozornění.

Atributy poskytují podrobné informace o nullovém stavu argumentů, návratových hodnot a členů instance objektu použitých k vyvolání členu. Podrobnosti o jednotlivých atributech najdete v referenčním článku jazyka o nullable referenčních atributech. Od verze .NET 5 jsou všechna rozhraní API modulu runtime .NET opatřena poznámkami. Statickou analýzu vylepšíte přidáním poznámek k rozhraním API za účelem poskytnutí sémantických informací o stavu null argumentů a návratových hodnotách.

Poznámky nulovatelných proměnných

Analýza nulového stavu poskytuje robustní analýzu místních proměnných. Kompilátor od vás potřebuje další informace pro členské proměnné. Kompilátor potřebuje další informace k nastavení stavu null všech polí v počáteční závorce člena. K inicializaci objektu lze použít kterýkoli z přístupných konstruktorů. Pokud by někdy mohlo být členské pole nastaveno na null, musí kompilátor předpokládat, že jeho nullový stav je možná-null na začátku každé metody.

Používáte poznámky, které mohou deklarovat, zda je proměnná s odkazovým typem, který může nabývat hodnoty null, nebo s odkazovým typem, který nemůže nabývat hodnoty null. Tyto poznámky dělají důležité prohlášení o stavu null pro proměnné:

  • Odkaz by neměl být null. Výchozí stav nenulové referenční proměnné není null. Kompilátor vynucuje pravidla, která zajišťují, že je bezpečné dereferencovat tyto proměnné, aniž by bylo nejprve zkontrolováno, že nejsou nulové.
    • Proměnná musí být inicializována na hodnotu, která není null.
    • Proměnnou nelze nikdy přiřadit hodnotu null. Kompilátor vydá upozornění, když kód přiřadí výraz možná null proměnné, která by neměla mít hodnotu null.
  • Odkaz může mít hodnotu null. Výchozí stav referenční proměnné s možnou hodnotou null je možná null. Kompilátor vynucuje pravidla, která zajistí, že správně zkontrolujete null odkaz:
    • Proměnnou lze dereferencovat pouze tehdy, když kompilátor může zaručit, že hodnota není null.
    • Tyto proměnné lze inicializovat s výchozí hodnotou null a je možné jí přiřadit null v jiném kódu.
    • Kompilátor nedává upozornění, když kód přiřadí výraz s možnou hodnotou null proměnné, která může mít hodnotu null.

Jakákoliv referenční proměnná, která není nulová, má počáteční stav nenulový. Každá referenční proměnná s možnou hodnotou null má počáteční stav null jako možná-null.

Typ nulovatelného odkazu je označen pomocí stejné syntaxe jako nulovatelné hodnotové typy: k typu proměnné se připojí ?. Například následující deklarace proměnné představuje proměnnou řetězce s možnou hodnotou null: name

C#
string? name;

Pokud jsou povoleny nulovatelné odkazové typy, každá proměnná, ke které není název typu doplněn ?, je nenulový odkazový typ. To zahrnuje všechny proměnné typu odkazu v existujícím kódu, jakmile tuto funkci povolíte. Nicméně, všechny implicitně zadané místní proměnné (deklarované pomocí var) jsou nulovatelné odkazové typy. Jak ukázaly předchozí části, statická analýza určuje null-stav místních proměnných, aby určila, jestli jsou možná nullové před jejich dereferencací.

Někdy je nutné přepsat upozornění, když víte, že proměnná není null, ale kompilátor určí její stav null jako možná null. Pomocí operátoru null-forgiving za názvem proměnné vynutíte, že stav null bude považován za nenulový. Například pokud víte, že proměnná name není null, ale kompilátor vydá upozornění, můžete napsat následující kód, kterým přepíšete analýzu kompilátoru:

C#
name!.Length;

Odkazové typy s možnou hodnotou null a typy hodnot null poskytují podobný sémantický koncept: Proměnná může představovat hodnotu nebo objekt, nebo tato proměnná může být null. Odkazové typy s možnou hodnotou null a typy hodnot null jsou však implementovány odlišně: typy hodnot s možnou hodnotou null jsou implementovány pomocí System.Nullable<T>a typy odkazů s možnou hodnotou null jsou implementovány atributy přečtenými kompilátorem. Například string? oba string jsou reprezentovány stejným typem: System.String. Avšak int? a int jsou reprezentovány System.Nullable<System.Int32> a System.Int32, respektive.

Odkazové typy s možnou hodnotou null jsou funkcí doby kompilace. To znamená, že volající může ignorovat upozornění, záměrně použít null jako argument metody, která očekává nenulový odkaz. Autoři knihoven by měli zahrnout kontroly prováděné za běhu proti hodnotám argumentů null. Upřednostňovanou ArgumentNullException.ThrowIfNull možností je kontrola parametru proti hodnotě null za běhu. Kromě toho je chování programu při použití poznámek o možných hodnotách null stejné, i když jsou všechny tyto poznámky (? a !) odstraněny. Jediným účelem je vyjádřit záměr návrhu a poskytnout informace pro analýzu stavu null.

Důležité

Povolení poznámek o nulovatelnosti může změnit způsob, jakým Entity Framework Core určuje, zda je datový prvek povinný. Další podrobnosti najdete v článku o základech Entity Framework Core: Práce s nulovatelnými referenčními typy.

Obecné typy

Obecné typy vyžadují podrobná pravidla pro zpracování T? libovolného parametru Ttypu . Pravidla jsou nutně podrobná kvůli historii a různé implementaci pro nulovatelný hodnotový typ a nulovatelný referenční typ. Nulovatelné datové typy jsou implementovány pomocí struktury System.Nullable<T>. Odkazové typy s možnou hodnotou null jsou implementovány jako poznámky typu, které kompilátoru poskytují sémantická pravidla.

  • Pokud je argument T odkazový typ, T? odkazuje na odpovídající odkazový typ s možností null. Například, pokud je Tstring, pak T? je string?.
  • Pokud je argument typu T typem hodnoty, T? odkazuje na stejný typ hodnotyT. Pokud je například Tint, pak T? je také int.
  • Pokud je argument T odkazového typu s možnou hodnotou null, T? odkazuje na stejný odkazový typ s možnou hodnotou null. Pokud je například Tstring?, pak T? je také string?.
  • Pokud je argument T nulovatelný typ hodnoty, T? odkazuje na tentýž nulovatelný typ hodnoty. Pokud je například Tint?, pak T? je také int?.

Pro návratové hodnoty je T? ekvivalentní s [MaybeNull]T; pro hodnoty argumentu je T? ekvivalentní s [AllowNull]T. Další informace najdete v článku o atributech pro analýzu stavu null v referenční dokumentaci jazyka.

Pomocí omezení můžete určit jiné chování:

  • Omezení class znamená, že T musí být nenulový odkazový typ (například string). Kompilátor vygeneruje upozornění, pokud použijete odkaz s možnou hodnotou null, například string? pro T.
  • Omezení class? znamená, že T musí být odkazovým typem, buď nenulovatelným typem odkazu (string), nebo typem odkazu s možnou hodnotou null (například string?). Pokud je parametr typu odkazem typu nullable, například string?, výraz T?, který odkazuje na stejný nullable referenční typ, například string?.
  • Omezení notnull znamená, že T musí být nenulový odkazový typ nebo nenulový typ hodnoty. Pokud pro parametr typu použijete nulovatelný referenční typ nebo nulovatelný hodnotový typ, kompilátor vygeneruje upozornění. Pokud je to T typ hodnoty, návratová hodnota je tento typ hodnoty, nikoli odpovídající typ hodnoty nullable.

Tato omezení pomáhají kompilátoru poskytnout další informace o tom, jak T se používá. To pomáhá, když vývojáři zvolí typ pro T a poskytuje lepší analýzu stavu null, když je použita instance obecného typu.

Kontext s možnou hodnotou null

Kontext s možností typu null určuje, jak se zpracovávají poznámky referenčního typu s možností null a jaká upozornění vytváří analýza statického stavu možnosti null. Kontext s možnou hodnotou null obsahuje dva příznaky: nastavení anotační a nastavení výstrahy.

Nastavení poznámek i upozornění jsou u stávajících projektů ve výchozím stavu zakázaná. Počínaje verzí .NET 6 (C# 10) jsou oba příznaky ve výchozím nastavení zapnuty pro nové projekty. Důvodem dvou odlišných příznaků pro nullable kontext je zjednodušit migraci velkých projektů, které existují před zavedením nullable referenčních typů.

U malých projektů můžete povolit odkazové typy s možnou hodnotou null, opravit upozornění a pokračovat. U větších projektů a řešení s více projekty ale může vygenerovat velký počet upozornění. Můžete použít pragmas k povolení odkazových typů s možnou hodnotou null po jednotlivých souborech, jakmile začnete používat nullable reference types. Nové funkce, které chrání před vyvoláním System.NullReferenceException , můžou být při zapnutí v existujícím základu kódu rušivé:

  • Všechny explicitní odkazové proměnné jsou interpretovány jako odkazové typy bez hodnoty null.
  • Význam omezení class v obecných typech se změnil tak, že nyní znamená nenulový referenční typ.
  • Kvůli těmto novým pravidlům se generují nová upozornění.

Kontext poznámek s možnou hodnotou null určuje chování kompilátoru. Existují čtyři kombinace nastavení u kontextu s hodnotou null:

  • Oba jsou zakázány: Kód je ignorující null hodnotu. Zakázat odpovídá chování před zapnutím odkazových typů s možnou hodnotou null, kromě toho, že nová syntaxe místo chyb vytváří upozornění.
    • Varování o nulovatelnosti jsou deaktivována.
    • Všechny proměnné typu odkazu jsou odkazové typy s možnou hodnotou null.
    • Použití přípony ? k deklaraci typu odkazu s možnou hodnotou null vytvoří upozornění.
    • Můžete použít operátor pro odpouštění null, !ale nemá žádný vliv.
  • povolené: Kompilátor povolí všechny analýzy nulových odkazů a všechny jazykové prvky.
    • Jsou povolena všechna nová upozornění s možnou hodnotou null.
    • Příponu ? můžete použít k deklaraci typu odkazu s možnou hodnotou null.
    • Proměnné typu odkazu bez ? přípony jsou odkazové typy bez hodnoty null.
    • Operátor pro tolerování null potlačí upozornění pro možnou dereferenci null.
  • upozornění povoleno: Kompilátor provede veškerou analýzu null a vygeneruje upozornění, kdy může dojít k dereferencování null.
    • Jsou povolena všechna nová upozornění s možnou hodnotou null.
    • Použití přípony ? k deklaraci typu odkazu s možnou hodnotou null vytvoří upozornění.
    • Všechny proměnné typu odkazu mohou mít hodnotu null. Členové však mají null-stavne-null na začátku složené závorky všech metod, pokud nejsou deklarováni s příponou ?.
    • Můžete použít operátor promíjející nulovou hodnotu, !.
  • povoleny poznámky: Kompilátor nevysílá upozornění, když kód může převést null, nebo když přiřadíte výraz s možnou hodnotou null proměnné, která není null.
    • Všechna nová upozornění s možnou hodnotou null jsou zakázaná.
    • Příponu ? můžete použít k deklaraci typu odkazu s možnou hodnotou null.
    • Reference type variables without the ? suffix are non-nullable reference types. Proměnné typu odkazu bez přípony ? jsou odkazové typy nepovolující hodnotu null.
    • Můžete použít operátor pro odpouštění null, !ale nemá žádný vliv.

Kontext anotace null a kontext varování null lze nastavit pro projekt pomocí elementu <Nullable> ve vašem souboru .csproj. Tento element konfiguruje, jak kompilátor interpretuje hodnotu nullability typů a jaká upozornění se vygenerují. Následující tabulka ukazuje povolené hodnoty a shrnuje kontexty, které zadávají.

Kontext Upozornění na dereference Upozornění přiřazení Typy odkazů ? přípona ! operátor
disable Deaktivováno Zakázáno Všechny jsou nulovatelné. Vyvolá upozornění. Nemá žádný vliv
enable Povoleno Povoleno Nenulové, pokud není deklarováno pomocí ? Deklaruje typ s možnou hodnotou null. Potlačí možná upozornění při přiřazení null.
warnings Povoleno Nelze použít Všechny jsou nullable, ale členy jsou považovány za nenulové při otevření složené závorky metod. Vyvolá upozornění. Potlačí upozornění na možné null přiřazení.
annotations Neaktivní Zakázáno Nelze nastavit na hodnotu null, pokud není deklarováno s ? Deklaruje nulovatelný typ. Nemá žádný vliv

Proměnné typu odkazu v kódu zkompilované v zakázaném kontextu jsou neplatné. Literál null nebo proměnnou s možnou hodnotou null můžete přiřadit proměnné, která je zamlžená. Výchozí stav proměnné s možnou hodnotou null však není null.

Můžete zvolit, které nastavení je pro váš projekt nejvhodnější:

  • Zvolte zakázat starší projekty, které nechcete aktualizovat na základě diagnostiky nebo nových funkcí.
  • Zvolte upozornění a určete, kde může kód vyvolat System.NullReferenceException. Tato upozornění můžete vyřešit před úpravou kódu, abyste povolili odkazové typy bez hodnoty null.
  • Zvolte poznámky k vyjádření vašeho záměru návrhu před povolením upozornění.
  • Zvolte povolit pro nové a aktivní projekty, u kterých se chcete chránit před výjimkami s odkazem na hodnotu null.

Příklad:

XML
<Nullable>enable</Nullable>

Direktivy můžete také použít k nastavení těchto stejných příznaků kdekoli ve zdrojovém kódu. Tyto direktivy jsou nejužitečnější při migraci velkého základu kódu.

  • #nullable enable: Nastaví příznaky poznámek a upozornění na povoleno.
  • #nullable disable: Nastaví příznaky poznámek a upozornění pro deaktivaci.
  • #nullable restore: Obnoví příznak poznámky a příznak upozornění do nastavení projektu.
  • #nullable disable warnings: Nastavte příznak upozornění na zakázat.
  • #nullable enable warnings: Nastavte příznak upozornění na pro aktivaci.
  • #nullable restore warnings: Obnoví příznak upozornění do nastavení projektu.
  • #nullable disable annotations: Nastavte příznak poznámek na zakázat.
  • #nullable enable annotations: Nastavte příznak poznámek na povolit.
  • #nullable restore annotations: Obnoví značku poznámky v nastavení projektu.

Pro libovolný řádek kódu můžete nastavit některou z následujících kombinací:

Příznak upozornění Příznak poznámky Používání
Výchozí nastavení projektu Výchozí nastavení projektu Výchozí
zapnout zakázat Odstraňte varování analýzy
povolit Výchozí nastavení projektu Opravit upozornění analýzy
Výchozí nastavení projektu povolit Přidání typových anotací
povolit umožnit Kód už migrovaný
zakázat povolit Označte kód poznámkami před opravou upozornění
zakázat zakázat Přidání staršího kódu do migrovaného projektu
Výchozí nastavení projektu zakázat Zřídka
zakázat Výchozí nastavení projektu Zřídka

Tyto devět kombinací poskytují jemně odstupňovanou kontrolu nad diagnostikou, kterou kompilátor generuje pro váš kód. V jakékoli oblasti, kterou aktualizujete, můžete povolit další funkce, aniž byste viděli další upozornění, která ještě nejste připravení řešit.

Důležité

Globální kontext s možnou hodnotou null se nevztahuje na vygenerované soubory kódu. V obou strategiích je kontext s možnou hodnotou null zakázán pro jakýkoli zdrojový soubor označený jako vygenerovaný. To znamená, že žádná rozhraní API ve vygenerovaných souborech nejsou opatřena poznámkami. Pro vygenerované soubory se nevytvářejí žádná upozornění na možnost hodnoty null. Soubor je označený jako vygenerovaný čtyřmi způsoby:

  1. V souboru .editorconfig zadejte generated_code = true v oddílu, který se vztahuje na tento soubor.
  2. Umístěte <auto-generated> nebo <auto-generated/> do komentáře v horní části souboru. Může být na libovolném řádku v daném komentáři, ale blok komentáře musí být prvním prvkem v souboru.
  3. Začněte název souboru s TemporaryGeneratedFile_
  4. Ukončete název souboru .designer.cs, .generated.cs, .g.cs nebo .g.i.cs.

Generátory se můžou přihlásit pomocí direktivy preprocesoru #nullable .

Ve výchozím nastavení jsou příznaky poznámek s možnou hodnotou null a upozornění zakázány. To znamená, že existující kód se zkompiluje beze změn a bez generování nových upozornění. Počínaje rozhraním .NET 6, nové projekty ve všech šablonách zahrnují prvek <Nullable>enable</Nullable>, přičemž tyto příznaky jsou nastaveny na povoleno.

Tyto možnosti poskytují dvě odlišné strategie aktualizace existujícího základu kódu tak, aby používaly odkazové typy s možnou hodnotou null.

Známé nástrahy

Pole a struktury, které obsahují odkazové typy, jsou známé nástrahy v odkazech s možnou hodnotou null a statická analýza, která určuje bezpečnost null. V obou situacích může být nepovolitelný odkaz inicializován na null, aniž by se vygenerovalo upozornění.

Struktury

Struktura, která obsahuje nenulové odkazové typy, umožňuje přiřazení default bez upozornění. Představte si následující příklad:

C#
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);
}

V předchozím příkladu není žádné upozornění v PrintStudent(default), zatímco nenulovatelné odkazové typy FirstName a LastName mají hodnotu null.

Dalším běžným případem je, když se zabýváte obecnými strukturami. Představte si následující příklad:

C#
#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;
    }
}

Za běhu je v předchozím příkladu vlastnost Propnull. Je přiřazený k nenulovatelnému řetězci bez upozornění.

Pole

Pole jsou také známým problémem v nullable reference types. Podívejte se na následující příklad, který nevygeneruje žádná upozornění:

C#
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());
    }
}

V předchozím příkladu deklarace pole ukazuje, že obsahuje nenulové řetězce, zatímco jeho prvky jsou všechny inicializovány na null. Potom je proměnná s přiřazena null hodnota (první prvek pole). Nakonec, když je proměnná s dereferencována, způsobí běhovou výjimku.

Konstruktory

Konstruktor třídy bude stále volat finalizátor, i když došlo k výjimce vyvolané tímto konstruktorem.
Následující příklad ukazuje toto chování:

C#
public class A
{
    private string _name;
    private B _b;

    public A(string name)
    {
        ArgumentNullException.ThrowIfNullOrEmpty(name);
        _name = name;
        _b = new B();
    }

  ~A()
  {
      Dispose();
  }

  public void Dispose()
  {
      _b.Dispose();
      GC.SuppressFinalize(this);
  }
}

public class B: IDisposable
{
    public void Dispose() { }
}

public void Main()
{
    var a = new A(string.Empty);
}

V předchozím příkladu bude System.NullReferenceException vyvoláno při spuštění _b.Dispose();, pokud byl parametr namenull. Volání _b.Dispose(); nikdy nevyvolá výjimku, pokud se konstruktor dokončí úspěšně. Kompilátor ale nevystavil žádné upozornění, protože statická analýza nemůže určit, jestli se metoda (jako konstruktor) dokončí bez vyvolání výjimky za běhu.

Viz také