Wyrażanie intencji
W poprzedniej lekcji przedstawiono, w jaki sposób kompilator języka C# może wykonywać analizę statyczną, aby chronić funkcję .NullReferenceException
Przedstawiono również sposób włączania kontekstu dopuszczanego do wartości null. W tej lekcji dowiesz się więcej na temat jawnego wyrażania intencji w kontekście dopuszczającym wartość null.
Deklarowanie zmiennych
Po włączeniu kontekstu dopuszczanego do wartości null masz lepszy wgląd w sposób, w jaki kompilator widzi kod. Możesz podjąć działania na ostrzeżenia wygenerowane na podstawie kontekstu z obsługą wartości null i w ten sposób jawnie definiujesz intencje. Na przykład kontynuujmy badanie kodu i przejmijmy FooBar
deklarację i przypisanie:
// Define as nullable
FooBar? fooBar = null;
Zanotuj dodany element ?
do FooBar
elementu . Informuje to kompilator, że jawnie zamierzasz fooBar
mieć wartość null. Jeśli nie zamierzasz fooBar
mieć wartości null, ale nadal chcesz uniknąć ostrzeżenia, rozważ następujące kwestie:
// Define as non-nullable, but tell compiler to ignore warning
// Same as FooBar fooBar = default!;
FooBar fooBar = null!;
W tym przykładzie dodano operator null-forgiving (!
) do null
elementu , który instruuje kompilator, że jawnie inicjujesz tę zmienną jako null. Kompilator nie będzie wystawiał ostrzeżeń o tym odwołaniu o wartości null.
Dobrym rozwiązaniem jest przypisanie zmiennych innych niż null,null
gdy są zadeklarowane, jeśli jest to możliwe:
// Define as non-nullable, assign using 'new' keyword
FooBar fooBar = new(Id: 1, Name: "Foo");
Operatory
Zgodnie z opisem w poprzedniej lekcji język C# definiuje kilka operatorów do wyrażania intencji wokół typów odwołań dopuszczanych do wartości null.
Operator null-forgiving (!
)
W poprzedniej sekcji przedstawiono operator forgiving o wartości null (!
). Informuje kompilator o zignorowaniu ostrzeżenia CS8600. Jest to jeden ze sposobów na powiedzenie kompilatorowi, że wiesz, co robisz, ale jest to zastrzeżenie, że w rzeczywistości powinieneś wiedzieć, co robisz!
Podczas inicjowania typów niezwiązanych z wartościami null podczas włączania kontekstu dopuszczalnego wartości null może być konieczne jawne zapytanie kompilatora o przebaczenie. Rozważmy na przykład następujący kod:
#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);
W poprzednim przykładzie FooBar fooBar = fooList.Find(f => f.Name == "Bar");
generuje ostrzeżenie CS8600, ponieważ Find
może zwrócić wartość null
. Możliwe null
byłoby przypisanie do fooBar
elementu , który jest niepusty w tym kontekście. Jednak w tym spisanym przykładzie wiemy, że Find
nigdy nie zwróci null
się zgodnie z zapisem. Tę intencję można wyrazić dla kompilatora za pomocą operatora forgiving o wartości null:
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!;
Zanotuj wartość !
na końcu .fooList.Find(f => f.Name == "Bar")
Informuje to kompilator, że wiadomo, że obiekt zwrócony przez Find
metodę może mieć null
wartość i jest w porządku.
Operator forgiving o wartości null można zastosować do wbudowanego obiektu przed wywołaniem metody lub oceną właściwości. Rozważmy inny spisany przykład:
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);
W powyższym przykładzie:
GetFooList
jest metodą statyczną, która zwraca typ dopuszczający wartość null,List<FooBar>?
.fooList
element ma przypisaną wartość zwracaną przezGetFooList
.- Kompilator generuje ostrzeżenie,
fooList.Find(f => f.Name == "Bar");
ponieważ wartość przypisana dofooList
elementu może mieć wartośćnull
. - Przy założeniu
fooList
, że parametr nienull
jest wartością , może zwrócić wartośćnull
,Find
ale wiemy, że tak nie będzie, więc jest stosowany operator forgiving o wartości null.
Aby wyłączyć ostrzeżenie, możesz zastosować operator fooList
forgiving o wartości null:
FooBar fooBar = fooList!.Find(f => f.Name == "Bar")!;
Uwaga
Należy używać operatora forgiving o wartości null w rozsądny sposób. Użycie go po prostu do odrzucenia ostrzeżenia oznacza, że mówisz kompilatorowi, aby nie ułatwić odnajdywania możliwych wpadek o wartości null. Używaj go oszczędnie i tylko wtedy, gdy masz pewność.
Aby uzyskać więcej informacji, zobacz ! Operator (null-forgiving) (odwołanie w C#).
Operator łączenia wartości null (??
)
Podczas pracy z typami dopuszczanymi wartościami null może być konieczne sprawdzenie, czy są one obecnie null
i podejmą określone działania. Jeśli na przykład przypisano null
typ dopuszczalny do wartości null lub jest niezainicjowany, może być konieczne przypisanie im wartości innej niż null. W tym miejscu przydaje się operator łączenia wartości null (??
).
Rozważmy następujący przykład:
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
salesTax ??= DefaultStateSalesTax.Value;
// Safely use salesTax object.
}
W poprzednim kodzie języka C#:
- Parametr
salesTax
jest definiowany jako dopuszczanyIStateSalesTax
do wartości null. - W treści
salesTax
metody parametr jest warunkowo przypisywany przy użyciu operatora łączenia wartości null.- Gwarantuje to, że jeśli
salesTax
została przekazana w taki sposóbnull
, że będzie miała wartość.
- Gwarantuje to, że jeśli
Napiwek
Jest to funkcjonalnie równoważne z następującym kodem języka C#:
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
if (salesTax is null)
{
salesTax = DefaultStateSalesTax.Value;
}
// Safely use salesTax object.
}
Oto przykład innego wspólnego idiomu języka C#, w którym może być przydatny operator łączenia wartości null:
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();
}
Poprzedni kod języka C#:
- Definiuje ogólną klasę otoki, w której parametr typu ogólnego jest ograniczony do
new()
klasy . - Konstruktor akceptuje
T source
parametr, który jest domyślnie ustawiony nanull
. - Opakowana
_source
jest warunkowo inicjowana do klasynew T()
.
Aby uzyskać więcej informacji, zobacz ?? i ?? = operatory (odwołanie w C#).
Operator warunkowy o wartości null (?.
)
Podczas pracy z typami dopuszczanymi wartościami null może być konieczne warunkowe wykonywanie akcji na podstawie stanu null
obiektu. Na przykład: w poprzedniej lekcji FooBar
rekord został użyty do zademonstrowania NullReferenceException
przez wyłudanie null
elementu . Zostało to spowodowane wywołaniami.ToString
Rozważmy ten sam przykład, ale teraz zastosuj operator warunkowy o wartości null:
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);
Poprzedni kod języka C#:
- Warunkowe wyłudzenie
fooBar
, przypisanie wynikuToString
do zmiennejstr
.- Zmienna
str
jest typustring?
(ciąg dopuszczalny do wartości null).
- Zmienna
- Zapisuje wartość
str
do standardowych danych wyjściowych, co nie jest niczym. - Wywołanie
Console.Write(null)
jest prawidłowe, więc nie ma żadnych ostrzeżeń. - Jeśli chcesz wywołać
Console.Write(str.Length)
metodę , zostanie wyświetlone ostrzeżenie, ponieważ potencjalnie wyłuszczenie wartości null.
Napiwek
Jest to funkcjonalnie równoważne z następującym kodem języka C#:
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);
Możesz połączyć operator, aby dodatkowo wyrazić swoją intencję. Można na przykład utworzyć łańcuch ?.
operatorów i ??
:
FooBar fooBar = null;
var str = fooBar?.ToString() ?? "unknown";
Console.Write(str); // output: unknown
Aby uzyskać więcej informacji, zapoznaj się z operatorami ?. i ?[] (warunkowe wartości null).
Podsumowanie
W tej lekcji przedstawiono sposób wyrażania intencji dopuszczalności null w kodzie. W następnej lekcji zastosujesz zdobytą wiedzę do istniejącego projektu.