Szándék kifejezése
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:
GetFooList
egy statikus metódus, amely null értékű típust ad vissza.List<FooBar>?
fooList
a függvény a visszaadott értéket rendeliGetFooList
hozzá.- A fordító figyelmeztetést hoz létre,
fooList.Find(f => f.Name == "Bar");
mert a hozzárendeltfooList
érték lehetnull
. - Feltételezve
fooList
, hogy nemnull
,Find
lehet , hogy visszatérnull
, 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álvaIStateSalesTax
. - 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, mertnull
annak értéke lesz.
- Ez biztosítja, hogy ha
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étertnull
. - A burkolt
_source
feltételesen inicializálva van egynew 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 null
haszná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éseToString
str
.- A
str
változó típusastring?
(null értékű sztring).
- A
- 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.