Megosztás:


Null értékű hivatkozástípusok

null értékű hivatkozástípusok olyan funkciók csoportja, amelyek minimalizálják annak valószínűségét, hogy a kód miatt a futtatókörnyezet System.NullReferenceExceptiondobjon. Három jellemző, amelyek segítenek elkerülni ezeket a kivételeket, beleértve annak képességét, hogy a hivatkozástípust explicit módon megjelöljük null értékű:

  • Továbbfejlesztett statikus áramláselemzés, amely meghatározza, hogy egy változó értéke lehet-e null a referencia nélkülözése előtt.
  • Olyan attribútumok, amelyek az API-kat úgy jegyzetelik meg, hogy a folyamatelemzés null állapotot határozzon meg.
  • Változójegyzetek, amelyekkel a fejlesztők explicit módon deklarálják a kívánt null-állapotot egy változóhoz.

A fordító a kód minden kifejezésének null állapotát követi nyomon fordításkor. A null állapotú két érték egyikével rendelkezik:

  • nem-null: A kifejezés ismerten nem-null.
  • maybe-null: A kifejezés lehet null.

A változók széljegyzetei határozzák meg egy referenciatípus változó nullságát :

  • nem null értékű: Ha egy értéket vagy egy talán-null kifejezést rendel a változóhoz, a fordító figyelmeztetést ad ki. A nem null értékű változók alapértelmezett null-állapota nem null.
  • null értékű: A változóhoz hozzárendelhet egy null értéket vagy egy talán-null kifejezést. Ha a változó esetleg nullállapota esetleg null, a fordító figyelmeztetést ad ki, ha dereferenciálja a változót. A változó alapértelmezett null állapota talán null.

A cikk további része azt ismerteti, hogyan működik ez a három funkcióterület, amikor a kód esetleg dereferál egy null értéket. Egy változó elhalasztása azt jelenti, hogy az egyik tagját a . (pont) operátorral érheti el, ahogyan az a következő példában látható:

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

Amikor dereferenciál egy változót, amelynek értéke null, a futtatókörnyezet egy System.NullReferenceException hibát dob.

Hasonlóképpen figyelmeztetések is előállíthatók, ha [] jelölést használják egy objektum egy tagjának elérésére, amikor az objektum null.

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
}

A következő tudnivalókat fogja elsajátítani:

  • A fordító nullállapot-elemzése: hogyan határozza meg a fordító, hogy egy kifejezés nem null vagy esetleg null.
  • Olyan API-kra alkalmazott attribútumok , amelyek több kontextust biztosítanak a fordító nullállapot-elemzéséhez.
  • Nullával jelölhető változójegyzetek, amelyek információt nyújtanak a változók kezelésének szándékáról. A széljegyzetek a mezők, paraméterek és visszatérési értékek esetében hasznosak az alapértelmezett nullállapot beállításához.
  • Az általános típusú argumentumokat szabályozó szabályok. Új megkötések lettek hozzáadva, mert a típusparaméterek hivatkozástípusok vagy értéktípusok lehetnek. Az ? utótag a null értékű értéktípusok és a null értékű referenciatípusok esetében eltérően van implementálva.
  • A nullázható környezet segít a nagy projektek migrálásában. Migráláskor engedélyezheti a figyelmeztetéseket és a széljegyzeteket a null értékű környezetben az alkalmazás egyes részeiben. A további figyelmeztetések kezelése után engedélyezheti a teljes projekt mindkét beállítását.

Végül megismerheti a nullállapot-elemzés ismert buktatóit a típusok és tömbök esetében struct .

Ezeket a fogalmakat a C# null értékű biztonságáról szóló Learn modulban is megismerheti.

Nullállapot-elemzés

nullállapot elemzése követi a hivatkozások nullállapotát . A kifejezések nem null értékűek , vagy talán null értékűek. A fordító kétféleképpen állapítja meg, hogy egy változó nem null értékű:

  1. A változóhoz olyan érték lett hozzárendelve, amely ismerten not-null.
  2. A változót ellenőrizték null-hoz képest, és azóta nem lett hozzárendelve semmihez.

Minden változó, amelyet a fordító nem tud meghatározni nem nulltalán null. Az elemzés figyelmeztetéseket ad olyan helyzetekben, amikor véletlenül dereferálhat egy null értéket. A fordító a null állapot alapján állít elő figyelmeztetéseket.

  • Ha egy változó nem null értékű, akkor arra a változóra biztonságosan lehet hivatkozni.
  • Ha egy változó talán null értékű, a változót ellenőrizni kell, hogy null ne legyen a halasztás előtt.

Vegyük a következő példát:

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

Az előző példában a fordító azt határozza meg, hogy message az első üzenet nyomtatásakor lehet-e null értékű. A második üzenetre nincs figyelmeztetés. A kód utolsó sora figyelmeztetést eredményez, mert originalMessage null értékű lehet. Az alábbi példa gyakorlatiasabb megoldást mutat be a csomópontok egy fájának a gyökérre való bejárására, és az egyes csomópontok feldolgozására a bejárás során:

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

Az előző kód nem generál figyelmeztetést a változó currentelhalasztására. A statikus elemzés meghatározza, hogy current soha nincs hivatkozva, amikor esetleg nullértékű. A változó current össze van vetve null, mielőtt hozzáférnének current.Parent-hoz, és mielőtt a current átadásra kerülne a ProcessNode művelethez. Az előző példák bemutatják, hogyan határozza meg a fordító a helyi változók null állapotát inicializálva, hozzárendelve vagy összehasonlítva null.

A nullállapot-elemzés nem követi a hívott metódusokat. Ennek eredményeképpen az összes konstruktor által meghívott, közös segédmetódusban inicializált mezők a következő üzenettel figyelmeztetést generálhatnak:

A "name" nem null értékű tulajdonságnak nem null értéket kell tartalmaznia a konstruktorból való kilépéskor.

Ezek a figyelmeztetések kétféleképpen kezelhetők: konstruktorláncolás vagy null értékű attribútumok a segédmetóduson. Az alábbi kód mindegyikre mutat példát. Az Person osztály az összes többi konstruktor által hívott közös konstruktort használja. Az Student osztály egy segédmetódussal rendelkezik, amely a System.Diagnostics.CodeAnalysis.MemberNotNullAttribute következő attribútummal van eljegyzve:


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

A null értékű állapot elemzése és a fordító által generált figyelmeztetések segítenek elkerülni a programhibákat a nulla hivatkozás megszüntetésével null. A null értékű figyelmeztetések feloldásáról szóló cikk technikákat biztosít a kódban valószínűleg megjelenő figyelmeztetések kijavítására. A nullállapot-elemzésből előállított diagnosztikák csak figyelmeztetések.

API-aláírások attribútumai

A nullállapot-elemzéshez fejlesztőktől származó tippekre van szükség az API-k szemantikájának megértéséhez. Egyes API-k null értékű ellenőrzéseket biztosítanak, és egy változó nullállapotát a maybe-null értékről a not-null értékre kell módosítani. Más API-k a bemeneti argumentumok null állapotától függően nem null vagy talán null értékű kifejezéseket adnak vissza. Vegyük például az alábbi kódot, amely nagybetűs üzenetet jelenít meg:

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

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

Az ellenőrzés alapján minden fejlesztő biztonságosnak tartja ezt a kódot, és nem generálhat figyelmeztetéseket. A fordító azonban nem tudja, hogy IsNull null ellenőrzést végez, és figyelmeztetést ad ki az message.ToUpper() utasításra, figyelembe véve, hogy a message egy lehet-null változó. A figyelmeztetés kijavításához használja az NotNullWhen attribútumot:

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

Ez az attribútum tájékoztatja a fordítót, hogy ha IsNull visszaadja false, a paraméter s nem null értékű. A fordító a blokkban megváltoztatja a null-állapot-ot a "nem-null" állapotra . A rendszer nem ad ki figyelmeztetést.

Az attribútumok részletes információkat nyújtanak az argumentumok null állapotáról, a visszatérési értékekről és a tag meghívásához használt objektumpéldány tagjairól. Az egyes attribútumok részletei a null értékű referenciaattribútumokról szóló nyelvi referenciacikkben találhatók. A .NET 5-től kezdődően minden .NET futtatókörnyezeti API annotálva van. A statikus elemzést úgy javíthatja, hogy az API-kat széljegyzetekkel javítja, hogy szemantikai információkat nyújtson az argumentumok nullállapotáról és az értékek visszaadásáról.

Null értékű változók annotációi

A nullállapot-elemzés robusztus elemzést biztosít a helyi változókhoz. A fordítónak további információra van szüksége a tagváltozókról. A fordítónak további információra van szüksége ahhoz, hogy az összes mező nullállapotú legyen egy tag nyitó zárójelében. Bármelyik akadálymentes konstruktor használható az objektum inicializálására. Ha egy tagmezőt valaha is beállíthatnak null, akkor a fordítónak feltételeznie kell, hogy a null-állapotatalán-null az egyes metódusok elején.

Olyan széljegyzeteket használ, amelyek deklarálhatják, hogy egy változó null értékű hivatkozástípusvagy nem null értékű hivatkozástípus. Ezek a széljegyzetek fontos állításokat tesznek a változók null állapotáról :

  • A hivatkozásnak nem szabad null értékűnek lennie. A nem null értékű referenciaváltozó alapértelmezett állapota nem null. A fordító olyan szabályokat kényszerít ki, amelyek biztosítják, hogy biztonságosan dereferenciálni lehessen ezeket az értékeket anélkül, hogy először ellenőrizné, nincs-e null értéke.
    • A változót nem null értékűre kell inicializálni.
    • A változó soha nem rendelhető hozzá az értékhez null. A fordító figyelmeztetést ad ki, ha a kód egy talán null értékű kifejezést rendel egy olyan változóhoz, amely nem lehet null értékű.
  • A hivatkozás null értékű lehet. A nullable referenciaváltozó alapértelmezett állapota lehet-null. A fordító szabályokat érvényesít annak biztosítása érdekében, hogy helyesen ellenőrizze egy null hivatkozás meglétét.
    • A változóra csak akkor lehet hivatkozni, ha a fordító garantálja, hogy az érték nem null.
    • Ezek a változók inicializálhatók az alapértelmezett null értékkel, és más kódban is hozzárendelhetők null értékhez.
    • A fordító nem ad ki figyelmeztetést, ha a kód egy null értékű változóhoz rendel egy talán null értékű kifejezést.

Bármely nem null értékű referenciaváltozónak a kezdeti null állapotanem null. A null értékű referenciaváltozók kezdeti null-állapota talán-null.

A null értékű hivatkozástípus a null értékű értéktípusokkal megegyező szintaxissal van feljegyezve: a ? jel hozzá van rendelve a változó típusához. A következő változódeklaráció például egy null értékű sztringváltozót jelöl: name

string? name;

Ha engedélyezve vannak a null értékű hivatkozástípusok, minden olyan változó, amelyben a ? típusnévhez nincs hozzáfűzve, nem null értékű hivatkozástípus. Ez magában foglalja a meglévő kód összes referenciatípus-változóját, miután engedélyezte ezt a funkciót. Az implicit módon beírt helyi változók (deklaráltak var) azonban null értékű referenciatípusok. Ahogy az előző szakaszok mutatták, a statikus elemzés meghatározza a helyi változók null állapotát, hogy megállapítsa, lehet-e null az előhívás előtt.

Néha figyelmen kívül kell hagynia egy figyelmeztetést, ha tudja, hogy egy változó nem null, de a fordító megállapítja, hogy a null-állapota esetleg null. A null-megengedő operátor használatával kényszerítheti egy változó null-állapotát arra, hogy nem null legyen. Ha például tudja, hogy a name változó nem null az, de a fordító figyelmeztetést ad ki, a következő kódot írhatja felül a fordító elemzésének felülbírálásához:

name!.Length;

A null értékű hivatkozástípusok és a null értékű értéktípusok hasonló szemantikai fogalmat biztosítanak: Egy változó egy értéket vagy objektumot jelölhet, vagy ez a változó lehet null. Azonban a nullable referenciatípusok és a nullable értéktípusok eltérően vannak implementálva: a nullable értéktípusok a System.Nullable<T> használatával vannak implementálva, míg a nullable referenciatípusokat a fordító által beolvasott attribútumokkal valósítják meg. Például, string? és string mindkettőt ugyanaz a típus képviseli: System.String. int? és int képviselve vannak System.Nullable<System.Int32> és System.Int32 által, illetve.

A null értékű hivatkozástípusok fordítási idő funkciónak számítanak. Lehetséges, hogy a hívók figyelmen kívül hagyják a figyelmeztetéseket, és szándékosan a null elemet használják argumentumként egy olyan metódushoz, amely nem null értékű hivatkozást vár. A kódtár-szerzőknek tartalmazniuk kell a null argumentumértékek futásidejű ellenőrzését. Ez ArgumentNullException.ThrowIfNull az előnyben részesített beállítás a paraméter null értékre való ellenőrzéséhez futásidőben. Ezenkívül a null értékű széljegyzeteket használó programok futásidejű viselkedése ugyanaz, ha az összes null értékű széljegyzet (? és !) el lesz távolítva. Az egyetlen céljuk a tervezési szándék kifejezése és a nullállapot-elemzéshez szükséges információk biztosítása.

Fontos

A nullálható annotációk engedélyezése megváltoztathatja, hogy az Entity Framework Core hogyan határozza meg, szükség van-e egy adattagra. További részleteket az Entity Framework Core alapjairól szóló cikkben talál: A null értékű referenciatípusok használata.

Generikus

Az általánosak részletes szabályokat igényelnek minden típusparaméter T?kezeléséhezT. A szabályok az előzmények és a null értékű értéktípusok eltérő implementációja és a null értékű hivatkozási típus miatt feltétlenül részletesek. A null értékű értéktípusok a System.Nullable<T> szerkezet használatával implementálhatók. A null értékű hivatkozástípusok olyan típusjegyzetekként vannak implementálva, amelyek szemantikai szabályokat biztosítanak a fordító számára.

  • Ha a T típusargumentum hivatkozástípus, akkor a T? a megfelelő nulla értékű hivatkozástípusra hivatkozik. Ha például T egy string, akkor T? egy string?.
  • Ha a típusargumentum T egy értéktípus, akkor T? ugyanarra az értéktípusra hivatkozik, T. Ha például T egy int, akkor az T? is egy int.
  • Ha a típusargumentum T null értékű hivatkozástípus, T? ugyanarra a null értékű hivatkozástípusra hivatkozik. Ha például T egy string?, akkor T? is egy string?.
  • Ha a típusargumentum T null értékű, T? ugyanarra a null értékű típusra hivatkozik. Ha például T egy int?, akkor T? is egy int?.

A visszatérési értékek esetében T? egyenértékű [MaybeNull]T-gyel; az argumentumértékek esetében T? egyenértékű [AllowNull]T-mal. További információ: Attribútumok a nullállapot-elemzéshez a nyelvi referenciaban.

A korlátozások használatával különböző viselkedést adhat meg:

  • A class kényszer azt jelenti, hogy T nem null értékű hivatkozástípusnak kell lennie (például string). A fordító figyelmeztetést ad, ha nullázható hivatkozástípust használ, mint például string? a T esetén.
  • A class? kényszer azt jelenti, hogy T referenciatípusnak kell lennie, vagy nem null értékű (string) vagy null értékű hivatkozástípusnak (például string?). Ha a típusparaméter null értékű hivatkozástípus, például string?olyan hivatkozási kifejezés T? , amely ugyanazzal a null értékű hivatkozástípussal rendelkezik, mint például string?.
  • A notnull kényszer azt jelenti, hogy T nem null értékű hivatkozástípusnak vagy nem null értékű értéktípusnak kell lennie. Ha null értékű hivatkozástípust vagy null értékű értéktípust használ a típusparaméterhez, a fordító figyelmeztetést hoz létre. Továbbá értéktípus esetén T a visszatérési érték az értéktípus, nem pedig a megfelelő null értékű típus.

Ezek a korlátozások további információt nyújtanak a fordítónak a használat módjáról T . Ez segít, ha a fejlesztők kiválasztják a típust T , és jobb nullállapot-elemzést biztosítanak az általános típus egy példányának használatakor.

Null értékkel rendelkezhető környezet

A null értékű környezet határozza meg, hogyan kezeli a null értékű hivatkozástípusú széljegyzeteket, és milyen figyelmeztetéseket hoz létre a statikus nullállapot-elemzés. 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 -ban (C# 10) kezdődően alapértelmezés szerint mindkét jelző engedélyezve van új projektekhez. 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 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.
    • Minden új nullálható figyelmeztetés engedélyezve.
    • ? 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 null értékű széljegyzetkörnyezet és a null értékű figyelmeztető környezet a .csproj fájl elemével<Nullable>állítható be egy projekthez. Ez az elem konfigurálja, hogy a fordító hogyan értelmezi a típusok nullképességét, és milyen figyelmeztetéseket ad ki. Az alábbi táblázat az engedélyezett értékeket mutatja be, és összegzi az általuk megadott környezeteket.

Környezet Referenciamegszüntetési figyelmeztetések Hozzárendelési figyelmeztetések Referenciatípusok ? toldalék ! operátor
disable Letiltva Letiltva Mindegyik lehet null értékű Figyelmeztetést ad Nincs hatása
enable Engedélyezve Engedélyezve 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élyezve 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 Letiltva Letiltva Nem null értékű, kivéve, ha ? Null értékkel rendelkező 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.

Kiválaszthatja, hogy melyik beállítás a legmegfelelőbb a projekthez:

  • 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: Állítsa a figyelmeztető jelzőt tiltásra.
  • #nullable enable warnings: Állítsa be a figyelmeztető jelzőt úgy, hogy engedélyezze.
  • #nullable restore warnings: Visszaállítja a figyelmeztető jelzőt a projekt beállításaira.
  • #nullable disable annotations: Állítsa a széljegyzetjelzőt úgy, hogy tiltsa le.
  • #nullable enable annotations: A széljegyzetjelző beállítása engedélyezze.
  • #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álat
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 azt jelenti, hogy a létrehozott fájlokban lévő API-k nincsenek széljegyzetekkel elosztva. A létrehozott fájlokra nem generálódnak nullértékű figyelmeztetések. A fájlokat négyféleképpen lehet generáltként megjelölni:

  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 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 .NET 6-tól kezdve az új projektek tartalmazzák az <Nullable>enable</Nullable> elemet az összes projektsablonban, és ezeket a jelzőket engedélyezetté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.

Ismert buktatók

A hivatkozástípusokat tartalmazó tömbök és szerkezetek ismert buktatók a null értékű hivatkozásokban, valamint a null biztonságot meghatározó statikus elemzések. Mindkét esetben előfordulhat, hogy a nem null értékű referencia inicializálva lesz null, figyelmeztetések létrehozása nélkül.

Struktúrák

A nem null értékű hivatkozástípusokat tartalmazó struktúra figyelmeztetések nélkül teszi lehetővé a hozzárendelést default . Vegyük a következő példát:

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

Az előző példában nincs figyelmeztetés PrintStudent(default)-ben, miközben a nem nullázható hivatkozástípusok, mint FirstName és LastName, null értékűek.

Egy másik gyakori eset, amikor általános struktúrákkal foglalkozik. Vegyük a következő példát:

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

Az előző példában a tulajdonság Prop futásidőben van null . Figyelmeztetés nélkül van hozzárendelve egy nem null értékű karakterlánchoz.

Tömbök

A tömbök a null értékű referenciatípusok ismert buktatói is. Tekintse meg az alábbi példát, amely nem eredményez figyelmeztetést:

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

Az előző példában a tömb deklarációja azt mutatja, hogy nem null értékű sztringeket tartalmaz, míg elemei mind inicializálva vannak.null Ezután a változóhoz s hozzá lesz rendelve egy null érték (a tömb első eleme). Végül a változó s derekonkrétálva van, ami futásidejű kivételt okoz.

Konstruktorok

Egy osztály konstruktora továbbra is meghívja a döntőst, még akkor is, ha a konstruktor kivételt jelzett.
Az alábbi példa ezt a viselkedést mutatja be:

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

Az előző példában a System.NullReferenceException dobódik, amikor a _b.Dispose(); fut, ha a name paraméter volt null. A hívás soha nem fog dobni _b.Dispose(); , ha a konstruktor sikeresen befejeződött. A fordító azonban nem adott ki figyelmeztetést, mert a statikus elemzés nem tudja megállapítani, hogy egy metódus (például egy konstruktor) futásidejű kivétel nélkül befejeződik-e.

Lásd még