Uttrycka avsikt
I föregående lektion lärde du dig hur C#-kompilatorn kan utföra statisk analys för att skydda mot NullReferenceException
. Du har också lärt dig hur du aktiverar en nullbar kontext. I den här lektionen lär du dig mer om att uttryckligen uttrycka din avsikt i en nullbar kontext.
Deklarera variabler
Med en nullbar kontext aktiverad har du mer insyn i hur kompilatorn ser din kod. Du kan agera på varningarna som genereras från en nullbar-aktiverad kontext, och när du gör det definierar du uttryckligen dina avsikter. Vi fortsätter till exempel att FooBar
undersöka koden och granska deklarationen och tilldelningen:
// Define as nullable
FooBar? fooBar = null;
Observera den ?
tillagda i FooBar
. Detta talar om för kompilatorn att du uttryckligen har för avsikt fooBar
att vara nullbar. Om du inte tänker fooBar
vara null, men ändå vill undvika varningen, bör du tänka på följande:
// Define as non-nullable, but tell compiler to ignore warning
// Same as FooBar fooBar = default!;
FooBar fooBar = null!;
Det här exemplet lägger till operatorn null-forgiving (!
) till null
, som instruerar kompilatorn att du uttryckligen initierar den här variabeln som null. Kompilatorn utfärdar inte varningar om att den här referensen är null.
En bra idé är att tilldela icke-nullbara variabler icke-värdennull
när de deklareras, om möjligt:
// Define as non-nullable, assign using 'new' keyword
FooBar fooBar = new(Id: 1, Name: "Foo");
Operatorer
Enligt beskrivningen i föregående lektion definierar C# flera operatorer för att uttrycka din avsikt kring nullbara referenstyper.
Null-förlåtande operator (!
)
Du introducerades för operatorn null-forgiving (!
) i föregående avsnitt. Den uppmanar kompilatorn att ignorera CS8600-varningen. Detta är ett sätt att berätta för kompilatorn att du vet vad du gör, men det kommer med förbehållet att du faktiskt borde veta vad du gör!
När du initierar icke-nullbara typer medan en nullbar kontext är aktiverad kan du behöva uttryckligen be kompilatorn om förlåtelse. Tänk till exempel på följande 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);
I föregående exempel FooBar fooBar = fooList.Find(f => f.Name == "Bar");
genererar en CS8600-varning, eftersom Find
kan returnera null
. Detta skulle null
kunna tilldelas till fooBar
, vilket inte kan null-värdet i den här kontexten. Men i det här invecklade exemplet vet vi att det aldrig kommer att Find
återvända null
som skrivet. Du kan uttrycka den här avsikten för kompilatorn med operatorn null-forgiving:
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!;
!
Observera i slutet av fooList.Find(f => f.Name == "Bar")
. Detta meddelar kompilatorn att du vet att objektet som returneras av Find
metoden kan vara null
, och att det är okej.
Du kan använda operatorn null-forgiving på ett objekt infogat före ett metodanrop eller egenskapsutvärdering. Överväg ett annat intrikat exempel:
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);
I exemplet ovan händer följande:
-
GetFooList
är en statisk metod som returnerar en nullbar typ,List<FooBar>?
. -
fooList
tilldelas värdet som returneras avGetFooList
. - Kompilatorn genererar en varning på
fooList.Find(f => f.Name == "Bar");
eftersom det tilldelade värdetfooList
kan varanull
. - Förutsatt att
fooList
intenull
är ,Find
kan returneranull
, men vi vet att det inte gör det, så operatorn null-forgiving tillämpas.
Du kan använda operatorn null-forgiving för att fooList
inaktivera varningen:
FooBar fooBar = fooList!.Find(f => f.Name == "Bar")!;
Kommentar
Du bör använda operatorn null-forgiving omdömesgillt. Om du bara använder den för att stänga en varning innebär det att du säger till kompilatorn att inte hjälpa dig att upptäcka möjliga null-missöden. Använd den sparsamt och endast när du är säker.
Mer information finns i ! (null-förlåtande) operator (C#-referens).
Operatorn Null-coalescing (??
)
När du arbetar med null-typer kan du behöva utvärdera om de är aktuella null
och vidta vissa åtgärder. Om en nullbar typ till exempel antingen har tilldelats null
eller om de är onitialiserade kan du behöva tilldela dem ett värde som inte är null. Det är där operatorn null-coalescing (??
) är användbar.
Ta följande som exempel:
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
salesTax ??= DefaultStateSalesTax.Value;
// Safely use salesTax object.
}
I föregående C#-kod:
- Parametern
salesTax
definieras som en nullbarIStateSalesTax
. - I metodtexten
salesTax
tilldelas villkorsstyrt med hjälp av operatorn null-coalescing.- Detta säkerställer att om
salesTax
skickades in somnull
att det kommer att ha ett värde.
- Detta säkerställer att om
Dricks
Detta motsvarar funktionellt följande C#-kod:
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
if (salesTax is null)
{
salesTax = DefaultStateSalesTax.Value;
}
// Safely use salesTax object.
}
Här är ett exempel på ett annat vanligt C#-formspråk där operatorn null-coalescing kan vara användbar:
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();
}
Föregående C#-kod:
- Definierar en allmän omslutningsklass, där den generiska typparametern är begränsad till
new()
. - Konstruktorn accepterar en
T source
parameter som är standardvärdet .null
- Den omslutna initieras
_source
villkorligt till ennew T()
.
Mer information finns i ?? och ?? = operatorer (C#-referens).
Null-villkorsstyrd operator (?.
)
När du arbetar med null-typer kan du behöva utföra åtgärder villkorligt baserat på tillståndet för ett null
objekt. Till exempel: i föregående enhet FooBar
användes posten för att demonstrera NullReferenceException
genom att dereferencing null
. Detta orsakades när det ToString
anropades. Tänk på samma exempel, men tillämpa nu den null-villkorsstyrda operatorn:
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);
Föregående C#-kod:
- Villkorsstyrd avreferering
fooBar
, tilldela resultatet avToString
till variabelnstr
.- Variabeln
str
är av typenstring?
(nullbar sträng).
- Variabeln
- Det skriver värdet
str
för till standardutdata, vilket inte är någonting. - Anropet
Console.Write(null)
är giltigt, så det finns inga varningar. - Du skulle få en varning om du skulle anropa
Console.Write(str.Length)
eftersom du potentiellt skulle avreferera null.
Dricks
Detta motsvarar funktionellt följande C#-kod:
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);
Du kan kombinera operatorn för att ytterligare uttrycka din avsikt. Du kan till exempel länka operatorerna ?.
och ??
:
FooBar fooBar = null;
var str = fooBar?.ToString() ?? "unknown";
Console.Write(str); // output: unknown
Mer information finns i operatorerna ?. och ?[] (null-conditional).
Sammanfattning
I den här lektionen har du lärt dig hur du uttrycker avsikten med nullabilitet i kod. I nästa lektion ska du tillämpa det du har lärt dig för ett befintligt projekt.