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 dereferencieras; det vill säga: 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, kallar null för "miljarddollarmisstaget."
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 en int är detta 0. För en DateTime är det DateTime.MinValue.
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:
- 
              firstberornullpå att referenstypenstringdeklarerades men ingen tilldelning gjordes.
- 
              secondtilldelasstring.Emptyi samband med att den deklareras. Objektet har aldrig haft någonnulltilldelning.
- 
              thirdär0trots att det inte är tilldelat. Det är enstruct(värdetyp) och har ettdefaultvärde av0.
- 
              dateär onitialiserad, men dessdefaultvärde är System.DateTime.MinValue.
Från och med C# 2.0 kan du definiera värdetyper som kan vara null med Nullable<T> (eller T? för genväg). 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ärnulleftersom den nullbara värdetypen är oinitialiserad.
- 
              secondtilldelasnulli samband med att den deklareras.
- 
              thirdärnullsom värdetdefaultförNullable<int>ärnull.
- 
              fourthär0eftersomnew()uttrycket anroparNullable<int>konstruktorn ochintär0som standardinställning.
C# 8.0 introducerade nullbara referenstyper, där du kan uttrycka din avsikt att en referenstyp kanske är 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 aldrignulleftersom det definitivt tilldelas.
- 
              secondbör aldrig varanull, även om det är från börjannull. Att utvärderasecondinnan den tilldelas något värde resulterar i en kompilatorvarning eftersom den är oinitialiserad.
- 
              thirdkan varanull. Det kan till exempel peka på enSystem.String, men det kan peka pånull. Någon av dessa varianter är godtagbara. Kompilatorn hjälper dig genom att varna dig om du avrefererarthirdutan att först kontrollera att det 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 nullbara 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 tilldelningen- null: C# Varning CS8600: Konvertering av nullliteral eller möjligt nullvärde till icke-nullbar typ.
- Linjen - _ = fooBar.ToString();har också en varning. Den här gången är kompilatorn orolig för att- fooBarkan vara null: 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.
 
              
              