Null-waarschuwingen oplossen

Tip

Nog niet bekend met nullable-verwijzingstypen? Lees eerst nullable referentietypen om annotaties en analyse van de null-status te begrijpen. In dit artikel wordt ervan uitgegaan dat u waarschuwingen ziet in een project waarvoor de functie is ingeschakeld.

Zoekt u een specifieke compilerfoutcode? In het artikel Met null-waarschuwingen oplossen wordt elke CS86xx-waarschuwing met de overeenkomende techniek catalogiseren.

Wanneer u null-referentietypen inschakelt, geeft de compiler waarschuwingen overal waar het gedrag van uw code niet overeenkomt met de aantekeningen. De meeste waarschuwingen vallen in een kleine set patronen. Zodra u het patroon herkent, is de oplossing meestal een van de vijf technieken:

  • Voeg een null-controle toe.
  • Een ?- of !-annotatie toevoegen of verwijderen.
  • Voeg een kenmerk toe dat het null-contract beschrijft.
  • Initialiseer variabelen correct.
  • Controleer de projectinstelling.

In dit artikel wordt elke techniek beschreven met een representatief voorbeeld. Het doel is geen waarschuwingen stil te leggen. Het doel is om de bedoeling van de null-afhandeling in de code expliciet te maken, zodat de compiler tot dezelfde conclusies komt als u.

Null-status: wat de compiler bijhoudt

Voordat u de technieken bekijkt, is het handig om te weten hoe de compiler potentiële schendingen van null-statussen bijhoudt. Terwijl de code wordt gelezen, houdt de compiler de null-status van elke expressie bij: de analyse over of de expressie zich op dat punt in de code kan bevinden null . De null-status is een van de twee waarden:

  • not-null : de compiler kan bewijzen dat de expressie hier niet null is. U kunt het veilig gebruiken zonder controle.
  • misschien-null : de compiler kan niet uitsluiten null. Als u de expressie gebruikt zonder te controleren, wordt er een waarschuwing gegenereerd.

De null-status van een variabele wordt gewijzigd wanneer de compiler uw code volgt. Een methode die mogelijk retourneert null , produceert mogelijk een null-resultaat . Een if (x is not null) controle beperkt x tot niet-null in het if blok. De waarschuwingen die u ziet, zijn de compiler die aangeeft dat een expressie een mogelijk null-status heeft en u staat op het punt deze te gebruiken alsof deze niet null was. Elke techniek in de rest van dit artikel is een andere manier om de compiler de informatie te geven die nodig is om ervoor te zorgen dat een expressie niet null is voordat u deze gebruikt.

Een null-controle toevoegen

De meest voorkomende waarschuwing is mogelijke dereferentie van null. De compiler heeft de null-status van een variabele bijgehouden naar misschien-null en zag de variabele die zonder controle werd gebruikt:

public static int LengthOfMessageUnsafe(string? message)
{
    // Warning CS8602: dereference of a possibly null reference.
    return message.Length;
}

De oplossing is meestal een guard clause. Een guard clause is een controle bovenaan een methode of blok die terugkeert of een exceptie genereert als de invoer ongeldig is. Alleen het veilige pad gaat verder. Zodra de controle wordt uitgevoerd, werkt de compiler de null-status van de variabele bij naar not-null op het veilige pad:

public static int DereferenceFixed(string? message)
{
    if (message is null)
    {
        return 0;
    }

    // No warning: the compiler knows message is not-null on this path.
    return message.Length;
}

Patroonkoppeling (expressies zoals is null of is { } die de vorm van een waarde testen) ??en ??= null-controles bevatten:

public static int NullOperatorsFix(string? message)
{
    // ?. evaluates to null if message is null; ?? supplies the fallback value.
    int length = message?.Length ?? 0;

    // Pattern matching narrows the type on the matching branch.
    if (message is { Length: > 0 })
    {
        length = message.Length;
    }

    return length;
}

Het eigenschapspatroon { Length: > 0 } komt alleen overeen wanneer message niet null is en de eigenschap Length ervan groter is dan nul, dus behandelt de compiler message als niet-null binnen het if-blok. Een eenvoudigere is not null-test levert dezelfde verfijning van de null-status op zonder eigenschappen te inspecteren.

Zie Null-operators voor een uitgebreide rondleiding door de operators.

Aantekeningen aanpassen

De compiler waarschuwt u ook wanneer uw code een misschien null-expressie toewijst aan een variabele die niet null kan worden uitgevoerd. Deze waarschuwing betekent een van de volgende twee dingen:

  • De variabele moet null-waarden toestaan. Voeg in dat geval een ? aan het type toe.
  • De expressie produceert nooit een null-waarde. Aantekeningen toevoegen aan de API die deze heeft geproduceerd.
public static void AssignmentWarning()
{
    // Warning CS8600: converting null literal or possible null value to non-nullable type.
    string name = Lookup("nobody");
    Console.WriteLine(name);
}

Als Lookup legitiem null retourneert, wijzigt u de aanroepplek zodat de ontbrekende waarde wordt geaccepteerd:

public static void AssignmentFixed()
{
    string? name = Lookup("somebody");
    if (name is not null)
    {
        Console.WriteLine(name);
    }
}

Als Lookup nooit null retourneert, wijzig dan de handtekening zodat deze een niet-nullbaar verwijzingstype retourneert. Scenario's waarin de null-status van de geretourneerde waarde afhankelijk is van de invoer, raadpleegt u de volgende sectie over null-analysekenmerken.

Gebruik de null-forgiving-operator ! alleen als u kunt garanderen dat een waarde niet null is, maar die garantie niet in het typesysteem kunt uitdrukken. Elk ! is een plaats waar de compiler u niet meer kan beveiligen, dus geef de voorkeur aan het toevoegen van een controle of aantekeningen bij de bron-API.

Een nullanalyseattribuut toevoegen

Soms bevindt de juiste oplossing zich niet op de oproepsite. De handtekening van een methode legt niet precies genoeg de relatie vast tussen de invoer en uitvoer, en de compiler geeft waarschuwingen binnen anders veilige code:

public static bool IsPresent(string? text) =>
    !string.IsNullOrEmpty(text);

public static void CallerWithoutAttribute(string? text)
{
    if (IsPresent(text))
    {
        // Warning CS8602: dereference of a possibly null reference.
        // The signature doesn't tell the compiler text is not-null here.
        Console.WriteLine(text.Length);
    }
}

De body van IsPresent bewijst dat het argument niet null is wanneer de methode true retourneert, maar de signatuur zegt dat niet. Voeg een null-analysekenmerk toe om het contractonderdeel van de API te maken:

public static bool AttributedIsPresent([NotNullWhen(true)] string? text) =>
    !string.IsNullOrEmpty(text);

public static void CallerWithAttribute(string? text)
{
    if (AttributedIsPresent(text))
    {
        // No warning: the attribute tells the compiler text is not-null.
        Console.WriteLine(text.Length);
    }
}

Algemene kenmerken zijn onder andere:

De volledige lijst staat in Nullable statische analyseattributen.

Niet-nullbare leden initialiseren

Een constructorwaarschuwing betekent dat een niet-null-veld, eigenschap of automatische eigenschap (een eigenschap die gebruikmaakt van het door compiler gegenereerde backingveld, zoals public string Name { get; set; }) de constructor verlaat zonder een niet-null-waarde toe te wijzen:

public class PersonUninitialized
{
    // Warning CS8618: Non-nullable property 'Name' is uninitialized.
    public string Name { get; set; }
}

U hebt verschillende manieren om dit aan te pakken. Kies degene die het beste overeenkomt met uw ontwerpintentie.

Stel de waarde verplicht als constructorargument. Gebruik een primaire constructor (parameters die zijn gedeclareerd voor het type zelf, beschikbaar in de hoofdtekst) of een reguliere constructor waarmee de eigenschap wordt geïnitialiseerd:

public class PersonInjected(string name)
{
    public string Name { get; } = name;
}

Maak de eigenschap required. De aanroeper moet het initialiseren via een object-initialisator (de syntaxis { Property = value } die volgt op new):

public class PersonRequired
{
    public required string Name { get; init; }
}

Initialiseer met een standaardwaarde. Wanneer het type een zinvolle lege waarde heeft, initialiseert u bij de declaratie:

public class PersonInitialized
{
    public string Name { get; set; } = "John Doe";
}

Tip

Kies deze techniek alleen als het type een echt goede standaardwaarde heeft: een geldig, volledig functioneel exemplaar dat bellers kunnen gebruiken. Voorbeelden hiervan zijn lege verzamelingen. Verzin geen sentinel (een plaatsaanduidingswaarde zoals String.Empty, "N/A", "unknown" of -1 die je als 'geen waarde' behandelt) als vervanging voor null: daarmee onderdruk je wel de waarschuwing, maar elke aanroeper moet van die sentinel weten en erop controleren, en het typesysteem kan niet helpen. Als er geen goede standaardwaarde bestaat, moet u in plaats daarvan de eigenschap nullable maken.

Maak de eigenschap nullbaar. Wanneer de waarde echt ontbreekt, wijzigt u het type in nullable:

public class PersonOptional
{
    public string? Name { get; set; }
}

Als een hulpmethode het lid initialiseert, annoteer de hulpmethode dan met MemberNotNullAttribute, zodat de compiler aanroepen ervan daaraan kan toeschrijven.

De projectinstelling controleren

Nieuwe C#-projecten maken standaard null-referentietypen mogelijk, dus de meeste code die u schrijft of leest, heeft de functie al ingeschakeld. Over het algemeen hoeft u niets te configureren. Als u wilt weten of een project deze optie heeft ingeschakeld, of als u de instelling wilt wijzigen, zoekt u in de .csproj naar het element <Nullable>:

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

De algemene ondersteunde waarden zijn enable (de standaardwaarde voor nieuwe projecten) en disable. Als het element ontbreekt, gebruikt het project de standaardinstelling die de SDK en het doelframework instellen.

Als u nullable wilt inschakelen voor slechts een deel van een bestand met #nullable instructies, of als u de gedeeltelijke warnings modus en annotations modi gebruikt bij het migreren van een bestaande codebasis, raadpleegt u De strategieën voor null-migratie.

Volgende stap

Wanneer een waarschuwing niet in een van deze patronen past, bevat het naslagartikel voor null-waarschuwingen oplossen de techniek voor elke CS86xx-waarschuwing die de compiler verzendt.

Als u een migratie wilt plannen waarmee null-referentietypen in een bestaande codebasis geleidelijk kunnen worden ingeschakeld, raadpleegt u De strategieën voor null-migratie.