Odkazové typy s možnou hodnotou null (referenční dokumentace jazyka C#)

Poznámka:

Tento článek popisuje odkazové typy s možnou hodnotou null. Můžete také deklarovat typy hodnot s možnou hodnotou null.

V kódu, který je v kontextu s možnou hodnotou null, použijte odkazové typy s možnou hodnotou null. Odkazové typy s možnou hodnotou null, upozornění statické analýzy null a operátor null-forgiving jsou volitelné jazykové funkce. Ve výchozím nastavení jsou všechny vypnuté. Kontext s možnou hodnotou null můžete řídit na úrovni projektu pomocí nastavení sestavení nebo kódu pomocí pragmas.

Referenční dokumentace jazyka C# dokumentuje naposledy vydané verze jazyka C#. Obsahuje také počáteční dokumentaci k funkcím ve verzi Public Preview pro nadcházející jazykovou verzi.

Dokumentace identifikuje všechny funkce, které byly poprvé představeny v posledních třech verzích jazyka nebo v aktuálních verzích Public Preview.

Návod

Informace o tom, kdy byla funkce poprvé představena v jazyce C#, najdete v článku o historii verzí jazyka C#.

Důležité

Všechny šablony projektu umožňují kontextu s možnou hodnotou null projektu. Projekty vytvořené pomocí dřívějších šablon tento prvek nezahrnují a tyto funkce jsou vypnuté, pokud je v souboru projektu nepovolíte nebo nepoužijete pragmas.

V kontextu s možnou hodnotou null:

  • Je nutné inicializovat proměnnou typu odkazu T s hodnotou, která není null, a nikdy nemůžete přiřadit hodnotu, která by mohla být null.
  • Proměnnou typu odkazu T? můžete inicializovat pomocí null nebo přiřadit null, ale před dereferencováním ji musíte zkontrolovat null .
  • Pokud použijete operátor null-progiving na proměnnou m typu T?, jako v m!, proměnná se považuje za nenulovou.

Kompilátor vynucuje rozdíly mezi nenulovým odkazovým typem T a typem T? odkazu s možnou hodnotou null pomocí předchozích pravidel. Proměnná typu T a proměnná typu T? jsou stejného typu .NET. Následující příklad deklaruje nenulový řetězec a řetězec s možnou hodnotou null a potom pomocí operátoru null-forgiving přiřadí hodnotu k řetězci, který není nullable:

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

Proměnné notNull i nullable oba používají String typ. Vzhledem k tomu, že typy s možnou hodnotou null a s možnou hodnotou null používají stejný typ, nemůžete použít odkaz s možnou hodnotou null v několika umístěních. Obecně nelze použít odkazový typ s možnou hodnotou null jako základní třídu nebo implementované rozhraní. V žádném objektovém vytvoření nebo testovacím výrazu typu nelze použít odkaz s možnou hodnotou null. Jako typ přístupového výrazu člena nemůžete použít odkaz s možnou hodnotou null. Následující příklady ukazují tyto konstrukce:

public MyClass : System.Object? // not allowed
{
}

var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
    if (thing is string? nullableString) // not allowed
        Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
    Console.WriteLine("error");
}

Odkazy s možnou hodnotou null a statická analýza

Příklady v předchozí části ilustrují povahu referenčních typů s možnou hodnotou null. Odkazové typy s možnou hodnotou null nejsou nové typy tříd, ale spíše poznámky k existujícím typům odkazů. Kompilátor tyto poznámky používá k nalezení potenciálních chyb odkazu null ve vašem kódu. Neexistuje žádný rozdíl mezi nenulovým odkazovým typem a typem odkazu s možnou hodnotou null. Kompilátor nepřidá žádnou kontrolu za běhu pro nenulové odkazové typy. Výhody jsou v analýze doby kompilace. Kompilátor generuje upozornění, která vám pomůžou najít a opravit potenciální chyby null v kódu. Deklarujete svůj záměr a kompilátor vás upozorní, když váš kód tento záměr porušuje.

Důležité

Poznámky odkazu s možnou hodnotou null nezavádějí změny chování, ale jiné knihovny můžou použít reflexi k vytvoření jiného chování modulu runtime pro odkazové typy s možnou hodnotou null a nenulovou. Entity Framework Core zejména čte atributy s možnou hodnotou null. Interpretuje odkaz s možnou hodnotou null jako volitelnou hodnotu a nenulový odkaz jako požadovanou hodnotu.

V kontextu s povolenou hodnotou null kompilátor provádí statickou analýzu proměnných libovolného typu odkazu, a to jak s možnou hodnotou null, tak bez hodnoty null. Kompilátor sleduje stav null každé referenční proměnné jako buď not-null , nebo možná-null. Výchozí stav odkazu bez hodnoty null není null. Výchozí stav odkazu s možnou hodnotou null je možná null.

Odkazové typy bez null by vždy měly být bezpečné pro dereference, protože jejich stav null není null. Pokud chcete toto pravidlo vynutit, kompilátor vydá upozornění, pokud není typ odkazu s možnou hodnotou null inicializován na hodnotu, která není null. Musíte přiřadit místní proměnné, kde je deklarujete. Každé pole musí mít přiřazenou hodnotu not-null v inicializátoru pole nebo každém konstruktoru. Kompilátor vydává upozornění, když je odkaz s možnou hodnotou null přiřazený k odkazu, jehož stav je možná null. Obecně platí, že nenulový odkaz není null a při dereferenci těchto proměnných se nevystavují žádná upozornění.

Poznámka:

Pokud přiřadíte výraz s možnou hodnotou null k nenulovatelnému typu odkazu, kompilátor vygeneruje upozornění. Kompilátor pak vygeneruje upozornění pro danou proměnnou, dokud není přiřazený k výrazu not-null .

Můžete inicializovat nebo přiřadit k referenčním typům s možnou hodnotou null null. Proto statická analýza musí před dereferencem určit, že proměnná nemá hodnotu null . Pokud je odkaz s možnou hodnotou null určen jako null, přiřaďte ho proměnné odkazu, která není nullable, vygeneruje upozornění kompilátoru. Následující třída ukazuje příklady těchto upozornění:

public class ProductDescription
{
    private string shortDescription;
    private string? detailedDescription;

    public ProductDescription() // Warning! shortDescription not initialized.
    {
    }

    public ProductDescription(string productDescription) =>
        this.shortDescription = productDescription;

    public void SetDescriptions(string productDescription, string? details=null)
    {
        shortDescription = productDescription;
        detailedDescription = details;
    }

    public string GetDescription()
    {
        if (detailedDescription.Length == 0) // Warning! dereference possible null
        {
            return shortDescription;
        }
        else
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
    }

    public string FullDescription()
    {
        if (detailedDescription == null)
        {
            return shortDescription;
        }
        else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
        return shortDescription;
    }
}

Následující fragment kódu ukazuje, kde kompilátor generuje upozornění při použití této třídy:

string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.

string description = "widget";
var item = new ProductDescription(description);

item.SetDescriptions(description, "These widgets will do everything.");

Předchozí příklady ukazují, jak statická analýza kompilátoru určuje stav null referenčních proměnných. Kompilátor používá pravidla jazyka pro kontroly a přiřazení null, aby informoval svou analýzu. Kompilátor nemůže předpokládat sémantiku metod nebo vlastností. Pokud voláte metody, které provádějí kontroly null, kompilátor nemůže tyto metody znát vliv na stav null proměnné. Do rozhraní API můžete přidat atributy, které kompilátoru informují o sémantice argumentů a návratových hodnotách. Mnoho běžných rozhraní API v knihovnách .NET má tyto atributy. Kompilátor například správně interpretuje IsNullOrEmpty jako kontrolu null. Další informace o atributech, které platí pro statickou analýzu stavu null, najdete v článku o atributech s možnou hodnotou Null.

Kontext s možnou hodnotou null

Kontext s možnou hodnotou null určuje, jak kompilátor zpracovává poznámky typu odkaz s možnou hodnotou null a jaká upozornění vytváří během statické analýzy stavu 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 víceprojektových řešení ale tento proces může generovat 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.
    • Proměnné typu odkazu bez ? přípony jsou odkazové typy bez hodnoty null.
    • Můžete použít operátor pro odpouštění null, !ale nemá žádný vliv.

Kontext poznámek s možnou hodnotou null a kontext upozornění s možnou hodnotou null pro projekt můžete nastavit pomocí elementu<Nullable> v souboru .csproj. Tento element konfiguruje, jak kompilátor interpretuje hodnotu nullability typů a jaká upozornění generuje. Následující tabulka ukazuje povolené hodnoty a shrnuje kontexty, které zadávají.

Context Upozornění na dereference Upozornění přiřazení Typy odkazů ? přípona ! operátor
disable Disabled Disabled Všechny jsou nulovatelné. Vyvolá upozornění. Nemá žádný vliv
enable Enabled Enabled 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 Enabled 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čí možná upozornění při přiřazení null.
annotations Disabled Disabled Nenulové, pokud není deklarováno pomocí ? Deklaruje typ s možnou hodnotou null. 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.

Zvolte nastavení, které nejlépe vyhovuje vašemu projektu:

  • 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 :

<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: Nastaví příznak upozornění, který chcete zakázat.
  • #nullable enable warnings: Nastaví příznak upozornění, který chcete povolit.
  • #nullable restore warnings: Obnoví příznak upozornění do nastavení projektu.
  • #nullable disable annotations: Nastaví příznak poznámky tak, aby se zakázal.
  • #nullable enable annotations: Nastaví příznak poznámky, který chcete 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 Use
Výchozí nastavení projektu Výchozí nastavení projektu Výchozí
povolit vypnout Odstraňte varování analýzy
povolit Výchozí nastavení projektu Odstraňte varování analýzy
Výchozí nastavení projektu povolit Přidání typových anotací
povolit povolit Kód už migrovaný
vypnout povolit Označte kód poznámkami před opravou upozornění
vypnout vypnout Přidání staršího kódu do migrovaného projektu
Výchozí nastavení projektu vypnout Zřídka
vypnout 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ý. Tato podmínka znamená, že kompilátor nenotuje žádná rozhraní API ve vygenerovaných souborech. Kompilátor negeneruje upozornění s možnou hodnotou null pro vygenerované soubory. Soubor se označí jako vygenerovaný některým z následujících čtyř způsobů:

  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 jako .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. Výchozí hodnota znamená, že váš 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.

Nastavení kontextu s možnou hodnotou null

Kontext s možnou hodnotou null můžete řídit dvěma způsoby. Na úrovni projektu přidejte <Nullable>enable</Nullable> nastavení projektu. Do jednoho zdrojového souboru jazyka C# přidejte direktivu #nullable enable pragma, která povolí kontext s možnou hodnotou null. Další informace najdete v tématu Nastavení strategie s možnou hodnotou null. Před .NET 6 používají nové projekty výchozí <Nullable>disable</Nullable>. Počínaje rozhraním .NET 6 zahrnují <Nullable>enable</Nullable> nové projekty prvek v souboru projektu.

Generika

Pokud použijete parametr typu, Tjako jeho protějšk s možnou hodnotou null, T?skutečný typ argument určuje způsob ? interpretace. Zvažte následující obecnou deklaraci:

public class Box<T>
{
    public T Contents { get; set; }
}

Protože parametr typu může stát buď pro typ odkazu nebo typ hodnoty, význam T? závisí na tom, který argument typu volající dodává. Následující pravidla popisují, co T? se řeší, když T nemá žádná omezení:

  • Argument typu je nenulový odkazový typ. T string Je Box<string>a T? je string?– odpovídající typ odkazu s možnou hodnotou null.
  • Argument typu je typ hodnoty. Pro Box<int>, T je int a T? je také int– stejný typ hodnoty. Poznámka nemá žádný vliv na typy hodnot, pokud parametr typu nemá struct omezení, v takovém případě T? znamená Nullable<T> (int?).
  • Argument typu je již nullable. Pro Box<string?>, T je string? a T? je stále string?. Nezískáte "doubly nullable" typ.

Omezení omezují, které argumenty typu jsou povolené. Umožňují také kompilátoru zjistit, jak T se dá použít:

  • where T : class vyžaduje odkazový typ bez hodnoty null. Box<string> je povoleno; Box<string?> vytvoří upozornění.
  • where T : class? umožňuje odkaz s možnou hodnotou null nebo nenulový odkaz. Obě Box<string> a Box<string?> jsou povoleny.
  • where T : struct vyžaduje typ hodnoty bez hodnoty null. Box<int> je povoleno; Box<int?> Není. S tímto omezením uvnitř T? obecných prostředků Nullable<T>— pro Box<int>, T? je int?.
  • where T : notnull vyžaduje nenulový odkaz nebo typ hodnoty. Box<string> a Box<int> jsou povoleny, Box<string?> vytvoří upozornění.
  • where T : BaseType vyžaduje odkazový typ, který není nullable, který je odvozen od BaseType. Připojte (where T : BaseType?) a povolte ? také odvozené typy s možnou hodnotou null.

Omezení pomáhají kompilátoru z důvodu, jak se používá parametr obecného typu:

public static T? FirstOrDefault<T>(IEnumerable<T> source)
{
    foreach (T item in source)
    {
        return item;
    }
    return default;
}

public static void RequireNotNull<T>(T value) where T : notnull
{
    ArgumentNullException.ThrowIfNull(value);
}

public static void Generics()
{
    string? first = FirstOrDefault<string>([]);
    Console.WriteLine(first ?? "<empty>");

    RequireNotNull("not null");
}

specifikace jazyka C#

Další informace naleznete v části Typy odkazů s možnou hodnotou Nullspecifikace jazyka C#.

Viz také