Null értékű hivatkozástípusok (C# hivatkozás)

Feljegyzés

Ez a cikk a null értékű hivatkozástípusokat ismerteti. Null értékű értéktípusokat is deklarálhat.

Null értékű hivatkozástípusokat használjon a null értékű tudatában lévő kódban. A null értékű hivatkozástípusok, a null statikus elemzési figyelmeztetések és a null-megbocsátó operátor választható nyelvi funkciók. Alapértelmezés szerint minden ki van kapcsolva. A null értékű környezeteket a projekt szintjén a build beállításaival vagy kódban, pragmák használatával szabályozhatja.

A C# nyelv referenciadokumentuma a C# nyelv legújabb kiadású verzióját ismerteti. Emellett a közelgő nyelvi kiadás nyilvános előzetes verziójú funkcióinak kezdeti dokumentációját is tartalmazza.

A dokumentáció azonosítja azokat a funkciókat, amelyeket először a nyelv utolsó három verziójában vagy az aktuális nyilvános előzetes verziókban vezetnek be.

Jótanács

Ha meg szeretné tudni, hogy mikor jelent meg először egy funkció a C#-ban, tekintse meg a C# nyelvi verzióelőzményeiről szóló cikket.

Fontos

Minden projektsablon engedélyezi a projekt számára a nullázható kontextus-t. A korábbi sablonokkal létrehozott projektek nem tartalmazzák ezt az elemet, és ezek a funkciók ki vannak kapcsolva, hacsak nem engedélyezi őket a projektfájlban, vagy nem használja a pragmákat.

Null értékű tudatos környezetben:

  • Nem null értékű referencia típusú T változót kell inicializálnia, és soha nem rendelhet hozzá olyan értéket, amely lehet null.
  • Egy referenciatípusú T?null változót inicializálhat vagy hozzárendelhet null, de a halasztás előtt ellenőriznie null kell.
  • Ha a null-elbocsátó operátort egy típusú mváltozóra T? alkalmazza, a m!változó nem null értékűnek minősül.

A fordító az előző szabályok használatával kényszeríti ki a nem null értékű referenciatípus T és a null értékű referenciatípus T? közötti különbségeket. A T típusú változók és a T? típusú változók a .NET-típussal azonosak. Az alábbi példa egy nem null értékű sztringet és egy null értékű karakterláncot deklarál, majd a null-elbocsátó operátor használatával rendel egy értéket egy nem null értékű sztringhez:

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

A változók notNull és nullable mindkettő a típust String használja. Mivel a nem null értékű és a null értékű típusok is ugyanazt a típust használják, több helyen nem használhat null értékű hivatkozástípust. Általában nem használhat null értékű hivatkozástípust alaposztályként vagy implementált felületként. Objektumlétrehozási vagy típustesztelési kifejezésben nem használhat null értékű hivatkozástípust. Taghozzáférés-kifejezés típusaként nem használhat null értékű hivatkozástípust. Az alábbi példák a következő szerkezeteket mutatják be:

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

Null értékű hivatkozások és statikus elemzés

Az előző szakaszban szereplő példák a null értékű hivatkozástípusok természetét szemléltetik. A null értékű hivatkozástípusok nem új osztálytípusok, hanem a meglévő referenciatípusokhoz tartozó széljegyzetek. A fordító ezeket a széljegyzeteket használva segít megtalálni a kódban esetleges nullhivatkozási hibákat. Nincs futásidejű különbség a nem null értékű referenciatípus és a null értékű referenciatípus között. A fordító nem ad hozzá futtatókörnyezet-ellenőrzést a nem null értékű referenciatípusokhoz. Az előnyök a fordítási idő elemzésében érhetők el. A fordító figyelmeztetéseket hoz létre, amelyek segítenek megtalálni és kijavítani a kód esetleges null hibáit. Deklarálja a szándékot, és a fordító figyelmezteti, ha a kód megsérti ezt a szándékot.

Fontos

A null értékű hivatkozási széljegyzetek nem vezetnek be viselkedésbeli változásokat, más kódtárak azonban a visszaverődést használva különböző futtatókörnyezeti viselkedést eredményezhetnek a null értékű és a nem null értékű referenciatípusokhoz. Az Entity Framework Core például null értékű attribútumokat olvas be. A null értékű hivatkozást opcionális értékként, a nem null értékű hivatkozást pedig kötelező értékként értelmezi.

Null értékű környezetben a fordító statikus elemzést végez bármilyen referenciatípusú változón, null értékű és nem null értékű változókon. A fordító az egyes referenciaváltozók null állapotát not-null vagy talán-null értékként követi nyomon. A nem null értékű hivatkozás alapértelmezett állapota nem null. A null értékű hivatkozás alapértelmezett állapota talán-null.

A nem null értékű hivatkozástípusoknak mindig biztonságosnak kell lenniük a hareferencia érdekében, mert null állapotuk nem null. A szabály kényszerítéséhez a fordító figyelmeztetéseket ad ki, ha egy nem null értékű hivatkozástípus nincs inicializálva nem null értékűre. Helyi változókat kell hozzárendelnie, ahol deklarálja őket. Minden mezőhöz nem null értéket kell hozzárendelni, egy mező inicializálójában vagy minden konstruktorban. A fordító figyelmeztetéseket ad ki, ha nem null értékű hivatkozás van hozzárendelve egy olyan hivatkozáshoz, amelynek az állapota talán null. A nem null értékű hivatkozások általában nem null értékűek , és nem jelennek meg figyelmeztetések a változók elhalasztásakor.

Feljegyzés

Ha nem null értékű hivatkozástípushoz rendel hozzá egy talán null értékű kifejezést, a fordító figyelmeztetést hoz létre. A fordító ezután figyelmeztetéseket hoz létre a változóhoz, amíg nem rendeli hozzá egy nem null értékű kifejezéshez.

Inicializálhatja vagy hozzárendelheti null a null értékű hivatkozástípusokat. Ezért a statikus elemzésnek meg kell állapítania, hogy egy változó nem null értékű a dereferencia előtt. Ha egy null értékű hivatkozás úgy van meghatározva, hogy talán null értékű, akkor a nem null értékű referenciaváltozóhoz való hozzárendelése fordítói figyelmeztetést eredményez. Az alábbi osztály példákat mutat be ezekre a figyelmeztetésekre:

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

Az alábbi kódrészlet bemutatja, hogy a fordító hol bocsát ki figyelmeztetéseket az osztály használatakor:

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.");

Az előző példák bemutatják, hogyan határozza meg a fordító statikus elemzése a referenciaváltozók nullállapotát . A fordító a null értékű ellenőrzésekre és hozzárendelésekre vonatkozó nyelvi szabályokat alkalmaz az elemzésének tájékoztatására. A fordító nem tud feltételezni a metódusok és tulajdonságok szemantikáját. Ha null értékű ellenőrzéseket végrehajtó metódusokat hív meg, a fordító nem tudja, hogy ezek a metódusok hatással vannak egy változó nullállapotára. Attribútumokat adhat az API-khoz, hogy tájékoztassa a fordítót az argumentumok szemantikáiról és az értékek visszaadásáról. A .NET-kódtárakban számos gyakori API rendelkezik ezekkel az attribútumokkal. A fordító például helyesen értelmezi a IsNullOrEmpty null értékű ellenőrzésként. A null állapotú statikus elemzésre alkalmazott attribútumokról további információt a Null értékű attribútumokról szóló cikkben talál.

Null értékkel rendelkezhető környezet

A null értékű környezet határozza meg, hogy a fordító hogyan kezeli a null értékű hivatkozástípus-széljegyzeteket, és milyen figyelmeztetéseket generál a statikus nullállapot-elemzés során. A null értékű környezet két jelölőt tartalmaz: a széljegyzet beállítást és a figyelmeztető beállítást.

A széljegyzet és figyelmeztető beállítások alapértelmezés szerint le vannak tiltva a meglévő projektek esetében. A .NET 6-os (C# 10)-ben kezdődően mindkét jelölő alapértelmezés szerint engedélyezve van new projektek esetében. A null értékű környezet két különböző jelzőjének oka az, hogy egyszerűbbé teszi a null értékű referenciatípusok bevezetését megelőző nagy projektek migrálását.

Kis projektek esetén engedélyezheti a null értékű hivatkozástípusokat, kijavíthatja a figyelmeztetéseket és folytathatja a műveletet. A nagyobb projektek és a többprojektes megoldások esetében azonban ez a folyamat számos figyelmeztetést eredményezhet. A pragmák használatával fájlonként engedélyezheti a null értékű hivatkozástípusokat a null értékű hivatkozástípusok használatának megkezdésekor. Az új funkciók, amelyek védelmet nyújtanak a System.NullReferenceException dobás ellen, zavaróak lehetnek, ha egy meglévő kódbázisban be van kapcsolva:

  • Minden explicit módon beírt referenciaváltozó nem null értékű referenciatípusként van értelmezve.
  • Az class kényszer értelmezése a generikus típusok esetén nem-null értékű hivatkozástípus jelentésére változott.
  • Az új szabályok miatt új figyelmeztetések jönnek létre.

A null értékű széljegyzetkörnyezet határozza meg a fordító viselkedését. A null értékű környezet beállításainak négy kombinációja van:

  • mindkét letiltott: A kód null értékű, nem kötelező. Állítsa vissza a viselkedést a null értékű hivatkozástípusok engedélyezése előtti állapotra, de az új szintaxis most hibák helyett figyelmeztetéseket generál.
    • Nullizálható figyelmeztetések le vannak tiltva.
    • Minden referenciatípus-változó null értékű referenciatípus.
    • ? Az utótag használata null értékű hivatkozástípus deklarálásához figyelmeztetést eredményez.
    • Használhatja a null-megbocsátó operátort, !, de nincs hatása.
  • mindkettő be van kapcsolva: A fordító bekapcsolja az összes nullhivatkozás-elemzést és a nyelvi funkciókat.
    • Az összes új, null értékeket megengedő figyelmeztetés engedélyezve van.
    • Az ? utótag használatával null értékű hivatkozástípust deklarálhat.
    • Az utótag nélküli ? referenciatípus-változók nem null értékű referenciatípusok.
    • A null megbocsátó operátor letiltja a figyelmeztetéseket a lehetséges dereferálás null esetén.
  • figyelmeztetés engedélyezett: A fordító minden nullérték-elemzést végrehajt, és figyelmeztetést ad ki, amikor a kód nullértéket próbál hivatkozni null.
    • Az összes új, null értékeket megengedő figyelmeztetés engedélyezve van.
    • ? Az utótag használata null értékű hivatkozástípus deklarálásához figyelmeztetést eredményez.
    • Minden referenciatípus-változó null értékű lehet. A tagok azonban az összes metódus nyitó zárójelében a not-null állapottalrendelkeznek, kivéve, ha az ? utótaggal van deklarálva.
    • Használhatja a null engedélyező operátort. !
  • széljegyzetek engedélyezve: A fordító nem ad ki figyelmeztetést, ha a kód esetleg dereferenciálja null-t, vagy ha egy esetleg null értékű kifejezést rendel hozzá egy nem nullázható változóhoz.
    • Minden új null értékekkel rendelkező figyelmeztetés le van tiltva.
    • Az ? utótag használatával null értékű hivatkozástípust deklarálhat.
    • Az utótag nélküli ? referenciatípus-változók nem null értékű referenciatípusok.
    • Használhatja a null-megbocsátó operátort, !, de nincs hatása.

A .csproj fájl elemével beállíthatja a projekt <Nullable> null értékű széljegyzetkörnyezetét és null értékű figyelmeztető környezetét. Ez az elem konfigurálja, hogy a fordító hogyan értelmezi a típusok nullképességét, és milyen figyelmeztetéseket bocsát ki. Az alábbi táblázat az engedélyezett értékeket mutatja be, és összegzi az általuk megadott környezeteket.

Context Referenciamegszüntetési figyelmeztetések Hozzárendelési figyelmeztetések Hivatkozástípusok ? toldalék ! operátor
disable Disabled Disabled Mindegyik lehet null értékű Figyelmeztetést ad Nincs hatása
enable Engedélyezett Engedélyezett Nem lehet null értékű, hacsak nincs deklarálva ?-ként. Null értékű típust deklarál Letiltja a lehetséges null hozzárendelésre vonatkozó figyelmeztetéseket
warnings Engedélyezett Nem alkalmazható Mindegyik null értékű, de a tagok nem null értékűnek minősülnek a metódusok kapcsos zárójelének megnyitásakor Figyelmeztetést ad Letiltja a lehetséges null hozzárendelésre vonatkozó figyelmeztetéseket
annotations Disabled Disabled Nem lehet null értékű, hacsak nincs deklarálva ?-ként. Null értékű típust deklarál Nincs hatása

A letiltott környezetben lefordított kód hivatkozástípus-változói null értékűek. Hozzárendelhet egy nullliterális vagy egy talán null értékű változót egy nullable-oblivious változóhoz. Azonban egy nullable-oblivious változó alapértelmezett állapota nem-null.

Válassza ki a projektnek leginkább megfelelő beállítást:

  • Válassza az olyan régi projektek letiltását , amelyeket nem szeretne frissíteni a diagnosztika vagy az új funkciók alapján.
  • Válassza a figyelmeztetéseket, hogy meghatározza, a kódja hol dobhat System.NullReferenceException. A nem null értékű hivatkozástípusok engedélyezéséhez a kód módosítása előtt kezelheti ezeket a figyelmeztetéseket.
  • A figyelmeztetések engedélyezése előtt válasszon széljegyzeteket a tervezési szándék kifejezéséhez.
  • Válassza az engedélyezés lehetőséget olyan új projektekhez és aktív projektekhez, amelyeknél védelmet szeretne nyújtani a nullhivatkozási kivételek ellen.

Példa:

<Nullable>enable</Nullable>

Az irányelvek használatával ezeket a jelzőket a forráskód bármely pontján beállíthatja. Ezek az irányelvek akkor hasznosak, ha nagy kódbázist migrál.

  • #nullable enable: Beállítja a széljegyzeteket és a figyelmeztető jelzőket, hogy engedélyezze.
  • #nullable disable: A széljegyzet- és figyelmeztetésjelzőket úgy állítja be, hogy tiltsa le.
  • #nullable restore: Visszaállítja a széljegyzetjelzőt és a figyelmeztető jelzőt a projekt beállításaihoz.
  • #nullable disable warnings: Beállítja a figyelmeztető jelző letiltását.
  • #nullable enable warnings: Beállítja a figyelmeztető jelző engedélyezését.
  • #nullable restore warnings: Visszaállítja a figyelmeztető jelzőt a projekt beállításaira.
  • #nullable disable annotations: Letiltja a széljegyzetjelzőt.
  • #nullable enable annotations: Beállítja a széljegyzetjelző engedélyezését.
  • #nullable restore annotations: Visszaállítja a széljegyzetjelzőt a projekt beállításaira.

Bármely kódsorhoz a következő kombinációk bármelyikét állíthatja be:

Figyelmeztető jelző Megjegyzésjelző Használd
projekt alapértelmezett beállítása projekt alapértelmezett beállítása Alapértelmezett
engedélyez letilt Elemzési figyelmeztetések javítása
engedélyez projekt alapértelmezett beállítása Elemzési figyelmeztetések javítása
projekt alapértelmezett beállítása engedélyez Típusjegyzetek hozzáadása
engedélyez engedélyez A kód már migrálva van
letilt engedélyez Kód jegyzetelése a figyelmeztetések javítása előtt
letilt letilt Örökölt kód hozzáadása a migrált projekthez
projekt alapértelmezett beállítása letilt Ritkán
letilt projekt alapértelmezett beállítása Ritkán

Ez a kilenc kombináció részletes vezérlést biztosít a fordító által a kódhoz kibocsátott diagnosztikák felett. A frissítendő területeken további funkciókat is engedélyezhet anélkül, hogy további figyelmeztetéseket látnál, amelyeket még nem tudsz kezelni.

Fontos

A globális null értékű környezet nem vonatkozik a létrehozott kódfájlokra. Mindkét stratégia esetében a null értékű környezet le van tiltva minden létrehozottként megjelölt forrásfájl esetében. Ez a feltétel azt jelenti, hogy a fordító nem jegyzetel semmilyen API-t a létrehozott fájlokban. A fordító nem állít elő null értékű figyelmeztetéseket a létrehozott fájlokra vonatkozóan. A fájlokat a következő négy módon jelölheti meg generáltként:

  1. A .editorconfig fájlban adja meg a generated_code = true értéket egy olyan szakaszban, amely az adott fájlra vonatkozik.
  2. Helyezzen <auto-generated> vagy <auto-generated/> egy megjegyzést a fájl tetejére. A megjegyzés bármely sorában szerepelhet, de a megjegyzésblokknak a fájl első elemének kell lennie.
  3. Kezdje a fájlnevet a következőkkel: TemporaryGeneratedFile_
  4. Fejezze be a fájlnevet .designer.cs, .generated.cs, .g.cs vagy .g.i.cs.

A generátorok az előfeldolgozási #nullable irányelv használatával is bejelentkezhetnek.

Alapértelmezés szerint a null értékű széljegyzetek és figyelmeztető jelzők le vannak tiltva. Ez az alapértelmezett érték azt jelenti, hogy a meglévő kód módosítások nélkül és új figyelmeztetések létrehozása nélkül fordítható le. A 6. .NET-tól kezdve az új projektek tartalmazzák az <Nullable>enable</Nullable> elemet az összes projektsablonban, és ezeket a jelzőket enabled értékre állítja.

Ezek a lehetőségek két különböző stratégiát biztosítanak egy meglévő kódbázis null értékű referenciatípusok használatára való frissítéséhez.

A null értékű környezet beállítása

A null értékű környezet kétféleképpen szabályozható. A projekt szintjén adja hozzá a projektbeállítást <Nullable>enable</Nullable> . Egyetlen C#-forrásfájlban adja hozzá a #nullable enable pragmát a null értékű környezet engedélyezéséhez. További információ: null értékű stratégia beállítása. A .NET 6 előtt az új projektek az alapértelmezettet használják, <Nullable>disable</Nullable>. A .NET 6-tól kezdődően az új projektek tartalmazzák a <Nullable>enable</Nullable> projektfájl elemét.

Általánosítások

Amikor egy típusparamétert használ, Tannak null értékű megfelelőjeként, T?a tényleges típusargumentum határozza meg, hogy a rendszer hogyan értelmezi a ? paramétert. Vegye figyelembe a következő általános deklarációt:

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

Mivel egy típusparaméter egy referenciatípus vagy egy értéktípus mellett is állhat, a jelentés T? attól függ, hogy a hívó melyik típusargumentumot adja meg. Az alábbi szabályok azt írják le, hogy mi T? oldható fel, ha T nincsenek korlátozások:

  • A típusargumentum nem null értékű hivatkozástípus. Az Box<string>, T is string és T? is string?– a megfelelő null értékű hivatkozástípus.
  • A típusargumentum értéktípus. Mert Box<int>az is intT és T? az is int– ugyanaz az értéktípus. A széljegyzetnek nincs hatása az értéktípusokra, hacsak a típusparaméter nem rendelkezik a struct korlátozással, ami esetben T? a (int?) értéket jelenti Nullable<T> .
  • A típusargumentum már null értékű. Mert Box<string?>, T van string? , és T? még mindig string?. Nem kap "kétszeresen null értékű" típust.

A korlátozások korlátozzák, hogy mely típusargumentumok engedélyezettek. Azt is lehetővé teszik a fordító számára, hogy hogyan T használható:

  • where T : class nem null értékű hivatkozástípust igényel. Box<string> engedélyezett; Box<string?> figyelmeztetést eredményez.
  • where T : class? null értékű vagy nem null értékű hivatkozástípust engedélyez. Mindkettő Box<string> és Box<string?> engedélyezett.
  • where T : struct nem null értékű értéktípust igényel. Box<int> engedélyezett; Box<int?> Nem. Ezzel a korlátozással T? az általános eszközökben Nullable<T>– például Box<int>az T? is – belül van int?.
  • where T : notnull nem null értékű hivatkozásra vagy értéktípusra van szükség. Box<string> és Box<int> engedélyezettek; Box<string?> figyelmeztetést eredményez.
  • where T : BaseType nem null értékű hivatkozástípust igényel, amely a forrásból BaseTypeszármazik. Hozzáfűzés ? (where T : BaseType?) a null értékű származtatott típusok engedélyezéséhez is.

A korlátozások segítenek a fordítónak az általános típusparaméterek használatával kapcsolatos okában:

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

C# nyelvspecifikáció

További információkért tekintse meg a C# nyelvspecifikációNullable referenciatípusokkal foglalkozó szakaszát.

Lásd még