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.

Odkazové typy s možnou hodnotou null jsou k dispozici v kódu, který se přihlásil k kontextu s podporou 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 se řídí na úrovni projektu pomocí nastavení sestavení nebo v kódu pomocí direktiv.

Důležité

Všechny šablony projektů začínající na .NET 6 (C# 10) umožňují pro projekt kontext s možnou hodnotou null. 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:

  • Proměnná typu odkazu T musí být inicializována s hodnotou non-null a nesmí být nikdy přiřazena hodnota, která může být null.
  • Proměnná typu odkazu T? může být inicializována nebo null přiřazena null, ale je nutné ji před zrušením odkazu zkontrolovat null .
  • Proměnná m typu T? je považována za nenulovou, pokud použijete operátor null-forgiving, jak je uvedeno v m!.

Rozdíly mezi nenulovým odkazovým typem T a typem T? odkazu s možnou hodnotou null jsou vynuceny interpretací kompilátoru předchozích pravidel. Proměnná typu T a proměnné typu T? jsou reprezentovány stejným typem .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 a nullable oba jsou reprezentovány typem String . Vzhledem k tomu, že typy s možnou hodnotou null a s možnou hodnotou null jsou uloženy jako stejný typ, existuje několik umístění, kde použití typu odkazu s možnou hodnotou null není povoleno. Obecně platí, že odkazový typ s možnou hodnotou null nelze použít jako základní třídu nebo implementované rozhraní. Typ odkazu s možnou hodnotou null nelze použít v žádném výrazu pro vytvoření objektu nebo testování typů. Odkazový typ s možnou hodnotou null nemůže být typem výrazu přístupu člena. 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.

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. Místní proměnné musí být přiřazeny tam, kde jsou deklarovány. 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 nejsou vydána žádná upozornění, pokud jsou tyto proměnné dereferenced.

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 .

Odkazové typy s možnou hodnotou null mohou být inicializovány nebo přiřazeny .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řazování 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. Tyto atributy byly použity pro mnoho běžných rozhraní API v knihovnách .NET Core. Například IsNullOrEmpty byla aktualizována a kompilátor správně interpretuje danou metodu 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.

Nastavení kontextu s možnou hodnotou null

Existují dva způsoby řízení kontextu s možnou hodnotou null. Na úrovni projektu můžete přidat <Nullable>enable</Nullable> nastavení projektu. V jednom zdrojovém souboru C# můžete přidat direktivu #nullable enable pragma, která povolí kontext s možnou hodnotou null. Přečtěte si článek o 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.

specifikace jazyka C#

Další informace najdete v následujících návrzích specifikace jazyka C#:

Viz také