Inzicht in null-baarheid

Voltooid

Als je een .NET-ontwikkelaar bent, heb je waarschijnlijk de System.NullReferenceException tegengekomen. Dit gebeurt tijdens de uitvoering wanneer een null wordt gedereferenceerd; dat wil zeggen, wanneer een variabele tijdens runtime wordt geëvalueerd, maar de variabele verwijst naar null. Deze uitzondering is verreweg de meest voorkomende uitzondering binnen het .NET-ecosysteem. De maker van null, Sir Tony Hoare, verwijst naar null de 'miljard dollar fout'.

In het volgende voorbeeld wordt de FooBar-variabele toegewezen aan null en onmiddellijk gedereferentieerd, waardoor het probleem optreedt.

// 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);

Het probleem wordt veel moeilijker te herkennen als ontwikkelaar wanneer uw apps groter en complexer worden. Het opsporen van mogelijke fouten zoals dit is een taak voor hulpprogramma's en de C#-compiler is er om u te helpen.

Null-veiligheid definiëren

De term null-veiligheid definieert een set functies die specifiek zijn voor null-typen die helpen het aantal mogelijke NullReferenceException exemplaren te verminderen.

In het vorige FooBar voorbeeld kunt u NullReferenceException vermijden door te controleren of de fooBar variabele null was voordat u deze derefereert.

// 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);

Om te helpen bij het identificeren van scenario's zoals deze, kan de compiler de intentie van uw code afleiden en het gewenste gedrag afdwingen. Dit is echter alleen wanneer een nullable context is ingeschakeld. Voordat u de null-context bespreekt, gaan we de mogelijke null-typen beschrijven.

Optionele typen

Vóór C# 2.0 waren alleen verwijzingstypen null-baar. Waardetypen zoals int of DateTime kunnen niet zijnnull. Als deze typen worden geïnitialiseerd zonder een waarde, vallen ze terug op hun default waarde. In het geval van een int, is dit 0. Voor een DateTime, het is DateTime.MinValue.

Verwijzingstypen die worden geïnstantieerd zonder initiële waarden werken anders. De default waarde voor alle referentietypen is null.

Bekijk het volgende C#-fragment:

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

In het voorgaande voorbeeld:

  • first is null omdat het referentietype string is gedeclareerd, maar er geen toewijzing is gemaakt.
  • second wordt string.Empty toegewezen wanneer het wordt gedeclareerd. Het object had nooit een null opdracht.
  • third is 0 ondanks dat het niet is toegewezen. Het is een struct (waardetype) en heeft een default waarde van 0.
  • date is niet geïnitialiseerd, maar de default waarde is System.DateTime.MinValue.

Vanaf C# 2.0 kunt u null-waardetypen definiëren met behulp van Nullable<T> (of T? voor afkorting). Hierdoor kunnen waardetypen null-baar zijn. Bekijk het volgende C#-fragment:

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

In het voorgaande voorbeeld:

  • first is null omdat het nulbare waardetype niet geïnitialiseerd is.
  • second wordt null toegewezen wanneer het wordt gedeclareerd.
  • third is null zoals de default waarde voor Nullable<int> is null.
  • Zoals de fourth-expressie de 0-constructor aanroept, is new()Nullable<int>, en is int standaard 0.

C# 8.0 heeft null-referentietypen geïntroduceerd, waarbij je kunt aangeven dat een referentietype mogelijk null kan zijn of altijd niet-null is. Misschien denkt u: "Ik dacht dat alle verwijzingstypen nullable zijn!" Je bent niet verkeerd, en dat zijn ze. Met deze functie kunt u uw intentie uitdrukken, die de compiler vervolgens probeert af te dwingen. Dezelfde T? syntaxis geeft aan dat een verwijzingstype bedoeld is om nullable te zijn.

Bekijk het volgende C#-fragment:

#nullable enable

string first = string.Empty;
string second;
string? third;

In het voorgaande voorbeeld wordt uw intentie als volgt afgeleid door de compiler:

  • first is nooitnull omdat het zeker is toegewezen.
  • second mag nooit zijnnull, ook al is het in eerste instantienull. Evalueren second voordat u een waarde toewijst, resulteert in een compilerwaarschuwing omdat deze niet geïnitialiseerd is.
  • third misschien welnull. Het kan bijvoorbeeld wijzen naar een , maar het kan ook wijzen naar System.String. Een van deze variaties is acceptabel. De compiler helpt u door u te waarschuwen als u de dereferentie third zonder eerst te controleren of deze geen null-waarde is.

Belangrijk

Als u de functie voor null-verwijzingstypen wilt gebruiken zoals hierboven wordt weergegeven, moet deze zich in een null-context bevinden. Dit wordt beschreven in de volgende sectie.

Null-context

Met null-contexten kunt u nauwkeurig bepalen hoe de compiler referentietypevariabelen interpreteert. Er zijn vier mogelijke null-contexten:

  • disable: De compiler gedraagt zich op dezelfde manier als C# 7.3 en eerder.
  • enable: De compiler maakt alle null-verwijzingsanalyse en alle taalfuncties mogelijk.
  • warnings: De compiler voert alle null-analyses uit en geeft waarschuwingen wanneer code mogelijk null kan dereferenceren.
  • annotations: de compiler voert geen null-analyse uit en geeft geen waarschuwingen wanneer de code null zou kunnen dereferenceren, maar u kunt uw code nog steeds annoteren met null-referentietypen ? en null-forgiving-operators (!).

Deze module is beperkt tot ofwel disable of enable nul-toelaatbare contexten. Raadpleeg voor meer informatie de referentietypen Nullable: Nullable contexten.

Null-verwijzingstypen inschakelen

Voeg in het C#-projectbestand (.csproj) een onderliggend <Nullable> knooppunt toe aan het <Project> element (of voeg toe aan een bestaand <PropertyGroup>). Hiermee wordt de enable null-context toegepast op het hele project.

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
    </PropertyGroup>

    <!-- Omitted for brevity -->

</Project>

U kunt ook een null-context toepassen op een C#-bestand met behulp van een compilerrichtlijn.

#nullable enable

De voorgaande C#-compilerrichtlijn is functioneel gelijk aan de projectconfiguratie, maar is beperkt tot het bestand waarin deze zich bevindt. Zie Null-referentietypen voor meer informatie: Null-contexten (docs)

Belangrijk

De null-context is standaard ingeschakeld in het .csproj-bestand in alle C#-projectsjablonen die beginnen met .NET 6.0 en hoger.

Wanneer de null-context is ingeschakeld, krijgt u nieuwe waarschuwingen. Bekijk het vorige FooBar voorbeeld, dat twee waarschuwingen bevat wanneer deze worden geanalyseerd in een nullable context:

  1. De FooBar fooBar = null; regel geeft een waarschuwing voor de null toewijzing: C# Waarschuwing CS8600: null-letterwaarde of mogelijke null-waarde converteren naar een niet-nulwaarde-type.

    Schermopname van C#-waarschuwing CS8600: null-literal of mogelijke null-waarde omzetten naar een niet-nullbare type.

  2. De _ = fooBar.ToString(); regel heeft ook een waarschuwing. Deze keer maakt de compiler zich zorgen dat fooBar mogelijk null kan zijn: C#-waarschuwing CS8602: Dereferencing van een mogelijk null-verwijzing.

    Schermopname van C#-waarschuwing CS8602: Deductie van een mogelijk null-verwijzing.

Belangrijk

Er is geen gegarandeerde null-veiligheid, zelfs als u op alle waarschuwingen reageert en deze elimineert. Er zijn enkele beperkte scenario's die de analyse van de compiler doorstaan, maar leiden tot een runtime NullReferenceException.

Samenvatting

In deze les hebt u geleerd om een nullable context in C# in te schakelen om u te beschermen tegen NullReferenceException. In de volgende eenheid leert u meer over het expliciet uitdrukken van uw intentie in een nullable context.