Förstå nullbarhet
Om du är .NET-utvecklare är chansen stor att du har stött på System.NullReferenceException. Detta inträffar vid körning när en null
är dereferenced, det vill: när en variabel utvärderas vid körning, men variabeln refererar till null
. Det här undantaget är det överlägset vanligaste undantaget i .NET-ekosystemet. Skaparen av null
, Sir Tony Hoare, refererar till null
som "miljard-dollar misstaget.".
I följande exempel tilldelas variabeln FooBar
till null
och avrefereras omedelbart, vilket visar problemet:
// Declare variable and assign it as null.
FooBar fooBar = null;
// Dereference variable by calling ToString.
// This will throw a NullReferenceException.
_ = fooBar.ToString();
// The FooBar type definition.
record FooBar(int Id, string Name);
Problemet blir mycket svårare att upptäcka som utvecklare när dina appar växer i storlek och komplexitet. Att upptäcka potentiella fel som detta är ett jobb för verktyg, och C#-kompilatorn är här för att hjälpa till.
Definiera nullsäkerhet
Termen nullsäkerhet definierar en uppsättning funktioner som är specifika för null-typer som hjälper till att minska antalet möjliga NullReferenceException
förekomster.
Med tanke på föregående FooBar
exempel kan du undvika NullReferenceException
genom att kontrollera om variabeln fooBar
var null
innan du avrefererar den:
// Declare variable and assign it as null.
FooBar fooBar = null;
// Check for null
if (fooBar is not null)
{
_ = fooBar.ToString();
}
// The FooBar type definition for example.
record FooBar(int Id, string Name);
För att hjälpa till med att identifiera scenarier som detta kan kompilatorn härleda avsikten med din kod och framtvinga önskat beteende. Detta är dock bara när en nullbar kontext är aktiverad. Innan vi diskuterar nullbar kontext ska vi beskriva de möjliga typerna av null-värden.
Typer som kan ogiltigas
Före C# 2.0 var endast referenstyper null. Värdetyper som int
eller DateTime
kunde inte vara null
. Om dessa typer initieras utan ett värde återgår de till sitt default
värde. När det gäller är int
detta 0
. För en DateTime
är DateTime.MinValue
det .
Referenstyper som instansieras utan inledande värden fungerar annorlunda. Värdet default
för alla referenstyper är null
.
Överväg följande C#-kodfragment:
string first; // first is null
string second = string.Empty // second is not null, instead it's an empty string ""
int third; // third is 0 because int is a value type
DateTime date; // date is DateTime.MinValue
I exemplet ovan händer följande:
first
berornull
på att referenstypenstring
deklarerades men ingen tilldelning gjordes.second
tilldelasstring.Empty
när den deklareras. Objektet har aldrig haft någonnull
tilldelning.third
är0
trots att den inte har tilldelats. Det är enstruct
(värdetyp) och har värdetdefault
0
.date
är onitialiserad, men dessdefault
värde är System.DateTime.MinValue.
Från och med C# 2.0 kan du definiera nullable value types using Nullable<T>
(eller T?
för shorthand). Detta gör att värdetyper kan vara nullbara. Överväg följande C#-kodfragment:
int? first; // first is implicitly null (uninitialized)
int? second = null; // second is explicitly null
int? third = default; // third is null as the default value for Nullable<Int32> is null
int? fourth = new(); // fourth is 0, since new calls the nullable constructor
I exemplet ovan händer följande:
first
berornull
på att den nullbara värdetypen är onitialiserad.second
tilldelasnull
när den deklareras.third
ärnull
som värdetdefault
förNullable<int>
ärnull
.fourth
är0
somnew()
uttrycket anroparNullable<int>
konstruktorn ochint
är0
som standard.
C# 8.0 introducerade nullbara referenstyper, där du kan uttrycka din avsikt att en referenstyp kan vara null
eller alltid är icke-null
. Du kanske tänker: "Jag trodde att alla referenstyper är nullbara!" Du har inte fel, och det är de. Med den här funktionen kan du uttrycka din avsikt, som kompilatorn sedan försöker tillämpa. Samma T?
syntax uttrycker att en referenstyp är avsedd att vara null.
Överväg följande C#-kodfragment:
#nullable enable
string first = string.Empty;
string second;
string? third;
Med hjälp av föregående exempel härleder kompilatorn din avsikt på följande sätt:
first
är aldrignull
som det definitivt tilldelas.second
bör aldrig varanull
, även om det är från börjannull
.second
Utvärdering innan du tilldelar ett värde resulterar i en kompilatorvarning eftersom den är onitialiserad.third
kan varanull
. Det kan till exempel peka på enSystem.String
, men den kan peka pånull
. Någon av dessa varianter är godtagbara. Kompilatorn hjälper dig genom att varna dig om du avrefererasthird
utan att först kontrollera att den inte är null.
Viktigt!
För att kunna använda funktionen för null-referenstyper som visas ovan måste den ligga inom en nullbar kontext. Detta beskrivs i nästa avsnitt.
Nullbar kontext
Nullbara kontexter möjliggör detaljerad kontroll för hur kompilatorn tolkar referenstypvariabler. Det finns fyra möjliga null-kontexter:
disable
: Kompilatorn fungerar på samma sätt som C# 7.3 och tidigare.enable
: Kompilatorn aktiverar alla null-referensanalyser och alla språkfunktioner.warnings
: Kompilatorn utför alla null-analyser och avger varningar när koden kan avrefereranull
.annotations
: Kompilatorn utför inte null-analys eller avger varningar när kod kan avrefereranull
, men du kan fortfarande kommentera koden med hjälp av null-referenstyper?
och null-förlåtande operatorer (!
).
Den här modulen är begränsad till antingen disable
eller enable
nullbara kontexter. Mer information finns i Referenstyper som kan ogiltigförklaras: Nullbara kontexter.
Aktivera null-referenstyper
I C#-projektfilen (.csproj) lägger du till en underordnad <Nullable>
nod i elementet <Project>
(eller lägger till i en befintlig <PropertyGroup>
). Detta tillämpar den enable
nullbara kontexten på hela projektet.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- Omitted for brevity -->
</Project>
Du kan också omfångsbegränsa nullbar kontext till en C#-fil med hjälp av ett kompileringsdirektiv.
#nullable enable
Det föregående C#-kompileringsdirektivet är funktionellt likvärdigt med projektkonfigurationen, men det är begränsat till filen där det finns. Mer information finns i Nullable reference types: Nullable contexts (docs)
Viktigt!
Den nullbara kontexten är aktiverad i .csproj-filen som standard i alla C#-projektmallar som börjar med .NET 6.0 och senare.
När den nullbara kontexten är aktiverad får du nya varningar. Tänk på föregående FooBar
exempel, som har två varningar när de analyseras i en nullbar kontext:
Raden
FooBar fooBar = null;
har en varning om tilldelningennull
: C# Varning CS8600: Konvertera nullliteral eller möjligt null-värde till icke-nullbar typ.Linjen
_ = fooBar.ToString();
har också en varning. Den här gången är kompilatorn orolig för attfooBar
den kan vara null: C# Warning CS8602: Dereference of a possibly null reference(C# Warning CS8602: Dereference of a possibly null reference).
Viktigt!
Det finns ingen garanterad null-säkerhet, även om du reagerar på och eliminerar alla varningar. Det finns vissa begränsade scenarier som kommer att klara kompilatorns analys, men ändå resultera i en körning NullReferenceException
.
Sammanfattning
I den här lektionen har du lärt dig att aktivera en nullbar kontext i C# för att skydda mot NullReferenceException
. I nästa lektion får du lära dig mer om att uttryckligen uttrycka din avsikt i en nullbar kontext.