Szándék kifejezése

Befejeződött

Az előző leckében megtanulta, hogyan képes a C#-fordító statikus elemzést végezni a védelem érdekében NullReferenceException. Azt is megtanulta, hogyan engedélyezheti a null értékű környezeteket. Ebben a leckében többet is megtudhat arról, hogyan fejezheti ki szándékát egy null értékű környezetben.

Változók deklarálása

Ha engedélyezve van a null értékű környezet, jobban láthatja, hogy a fordító hogyan látja a kódot. A null értékű környezetből generált figyelmeztetésekre reagálva explicit módon határozza meg a szándékait. Például folytassuk a FooBar kód vizsgálatát, és vizsgáljuk meg a deklarációt és a hozzárendelést:

// Define as nullable
FooBar? fooBar = null;

Jegyezze fel a ? hozzáadott elemet FooBar. Ez tájékoztatja a fordítót arról, hogy kifejezetten null értékűre fooBar kívánja állítani a fordítót. Ha nem szeretné fooBar , hogy null értékű legyen, de továbbra is el szeretné kerülni a figyelmeztetést, vegye figyelembe az alábbiakat:

// Define as non-nullable, but tell compiler to ignore warning
// Same as FooBar fooBar = default!;
FooBar fooBar = null!;

Ez a példa hozzáadja a null-megbocsátó (!) operátort a függvényhez null, amely arra utasítja a fordítót, hogy explicit módon inicializálja a változót null értékként. A fordító nem ad ki figyelmeztetést arról, hogy a hivatkozás null értékű.

A nem null értékű változók deklarálásakor ajánlott a nem null értékű változóknull hozzárendelése, ha lehetséges:

// Define as non-nullable, assign using 'new' keyword
FooBar fooBar = new(Id: 1, Name: "Foo");

Operátorok

Ahogy az előző leckében is szó volt róla, a C# több operátort is meghatároz, hogy kifejezze szándékát a null értékű referenciatípusok körül.

Null-megbocsátó (!) operátor

Az előző szakaszban bevezettük a null-elbocsátó operátort (!). Arra utasítja a fordítót, hogy hagyja figyelmen kívül a CS8600 figyelmeztetést. Ez az egyik módja annak, hogy elmondja a fordítónak, hogy tudja, mit csinál, de azzal a kikötéssel jár, hogy valójában tudnia kell , hogy mit csinál!

Ha nem null értékű típusokat inicializál, miközben engedélyezve van a null értékű környezet, előfordulhat, hogy explicit módon meg kell kérnie a fordítót a megbocsátásért. Vegyük például a következő kódot:

#nullable enable

using System.Collections.Generic;

var fooList = new List<FooBar>
{
    new(Id: 1, Name: "Foo"),
    new(Id: 2, Name: "Bar")
};

FooBar fooBar = fooList.Find(f => f.Name == "Bar");

// The FooBar type definition for example.
record FooBar(int Id, string Name);

Az előző példában FooBar fooBar = fooList.Find(f => f.Name == "Bar"); egy CS8600-figyelmeztetést hoz létre, mert Find az visszaadhat null. Ez a lehetőség null hozzá lesz rendelve fooBar, amely ebben a kontextusban nem null értékű. Ebben a példában azonban tudjuk, hogy ez Find soha nem fog visszatérni null írott módon. Ezt a szándékot a null-megbocsátó operátorral fejezheti ki a fordítónak:

FooBar fooBar = fooList.Find(f => f.Name == "Bar")!;

Jegyezze fel a ! végén fooList.Find(f => f.Name == "Bar"). Ez azt jelzi a fordítónak, hogy tudja, hogy a Find metódus által visszaadott objektum lehet null, és ez rendben van.

A null-megbocsátó operátort alkalmazhatja egy beágyazott objektumra a metódushívás vagy a tulajdonság kiértékelése előtt is. Vegyünk egy másik konvenciós példát:

List<FooBar>? fooList = FooListFactory.GetFooList();

// Declare variable and assign it as null.
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!; // generates warning

static class FooListFactory
{
    public static List<FooBar>? GetFooList() =>
        new List<FooBar>
        {
            new(Id: 1, Name: "Foo"),
            new(Id: 2, Name: "Bar")
        };
}

// The FooBar type definition for example.
record FooBar(int Id, string Name);

Az előző példában:

  • GetFooListegy statikus metódus, amely null értékű típust ad vissza. List<FooBar>?
  • fooList a függvény a visszaadott értéket rendeli GetFooListhozzá.
  • A fordító figyelmeztetést hoz létre, fooList.Find(f => f.Name == "Bar"); mert a hozzárendelt fooList érték lehet null.
  • Feltételezve fooList , hogy nem null, Find lehet , hogy visszatér null, de tudjuk, hogy nem, így a null-megbocsátó operátor lesz alkalmazva.

A figyelmeztetés letiltásához a null-elbocsátó operátort fooList alkalmazhatja:

FooBar fooBar = fooList!.Find(f => f.Name == "Bar")!;

Feljegyzés

A null-megbocsátó operátort célszerű megfontolni. Ha egyszerűen elvet egy figyelmeztetést, az azt jelenti, hogy azt mondja a fordítónak, hogy ne segítsen felderíteni a lehetséges null eltéréseket. Használja takarékosan, és csak akkor, ha biztos benne.

További információkért tekintse meg a következőt : (null-forgiving) operátor (C# hivatkozás).

Null-szenesítési (??) operátor

Null értékű típusok használatakor előfordulhat, hogy ki kell értékelnie, hogy jelenleg null vannak-e, és bizonyos műveletet kell elvégeznie. Ha például null értékű típus van hozzárendelve null , vagy nem inicializálódott, előfordulhat, hogy nem null értéket kell hozzájuk rendelnie. Ez az, ahol a null-szenesítő operátor (??) hasznos.

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

public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
    salesTax ??= DefaultStateSalesTax.Value;

    // Safely use salesTax object.
}

Az előző C# kódban:

  • A salesTax paraméter null értékűként van definiálva IStateSalesTax.
  • A metódus törzsén belül a salesTax rendszer feltételesen hozzárendeli a null-szenesítő operátort.
    • Ez biztosítja, hogy ha salesTax át lett adva, mert null annak értéke lesz.

Tipp.

Ez funkcionálisan egyenértékű a következő C#-kóddal:

public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
    if (salesTax is null)
    {
        salesTax = DefaultStateSalesTax.Value;
    }

    // Safely use salesTax object.
}

Íme egy példa egy másik gyakori C#-kifejezésre, ahol a null-szenesítés operátor hasznos lehet:

public sealed class Wrapper<T> where T : new()
{
    private T _source;

    // If given a source, wrap it. Otherwise, wrap a new source:
    public Wrapper(T source = null) => _source = source ?? new T();
}

Az előző C# kód:

  • Egy általános burkolóosztályt határoz meg, amelyben az általános típusparamétert a rendszer korlátozza new().
  • A konstruktor elfogad egy T source alapértelmezett paramétert null.
  • A burkolt _source feltételesen inicializálva van egy new T().

További információ: ?? és ?? = operátorok (C#-referencia).

Null feltételes (?.) operátor

Null értékű típusok használatakor előfordulhat, hogy egy objektum állapota alapján feltételesen végre kell hajtania null a műveleteket. Például: az előző leckében a FooBar rekordot a dereferencing nullhasználatával mutatták beNullReferenceException. Ezt a hívása ToString okozta. Vegyük ezt a példát, de most alkalmazzuk a null-feltételes operátort:

using System;

// Declare variable and assign it as null.
FooBar fooBar = null;

// Conditionally dereference variable.
var str = fooBar?.ToString();
Console.Write(str);

// The FooBar type definition.
record FooBar(int Id, string Name);

Az előző C# kód:

  • Feltételesen halasztások fooBar, a változó eredményének hozzárendelése ToString str .
    • A str változó típusa string? (null értékű sztring).
  • A standard kimenet értékét str írja, ami semmi.
  • A hívás Console.Write(null) érvényes, ezért nincsenek figyelmeztetések.
  • Figyelmeztetést kapna, ha meghívná Console.Write(str.Length) , mert lehetséges, hogy a null értéket késlelteti.

Tipp.

Ez funkcionálisan egyenértékű a következő C#-kóddal:

using System;

// Declare variable and assign it as null.
FooBar fooBar = null;

// Conditionally dereference variable.
string str = (fooBar is not null) ? fooBar.ToString() : default;
Console.Write(str);

// The FooBar type definition.
record FooBar(int Id, string Name);

A szándék további kifejezéséhez kombinálhatja az operátort. Láncra kapcsolhatja például az és ?? az ?. operátorokat:

FooBar fooBar = null;
var str = fooBar?.ToString() ?? "unknown";
Console.Write(str); // output: unknown

További információ: ?. és ?[] (null-feltételes) operátorok.

Összegzés

Ebben a leckében megismerkedett a nullability szándék kódban való kifejezésével. A következő leckében a tanultakat fogja alkalmazni egy meglévő projektre.

Tesztelje tudását

1.

Mi a default referenciatípus értéke string ?

2.

Mi a halasztás várható viselkedése null?

3.

Mi történik a throw null; C#-kód végrehajtásakor?

4.

Melyik állítás a legpontosabb a null értékű hivatkozástípusokra vonatkozóan?