Lös nullbarhetsvarningar

Tip

Ny på nullbara referenstyper? Läs först Nullbara referenstyper för att förstå annoteringar och analys av null-tillstånd. Den här artikeln förutsätter att du ser varningar i ett projekt där funktionen är aktiverad.

Letar du efter en specifik kompilatorfelkod? Referensartikeln Lös nullbara varningar katalogiserar varje CS86xx-varning med matchningstekniken.

När du aktiverar nullbara referenstyper utfärdar kompilatorn varningar överallt där kodens beteende inte matchar dess anteckningar. De flesta varningar hamnar i en liten uppsättning mönster. När du känner igen mönstret är korrigeringen vanligtvis en av fem tekniker:

  • Lägg till en null-kontroll.
  • Lägg till eller ta bort en ? eller ! kommentar.
  • Lägg till ett attribut som beskriver null-kontraktet.
  • Initiera variabler korrekt.
  • Verifiera projektinställningen.

Den här artikeln går igenom varje teknik med ett representativt exempel. Målet är inte att tysta varningar. Det är för att göra kodens avsikt för nullhantering explicit så att kompilatorn når samma slutsatser som du gör.

Null-tillstånd: vad kompilatorn spårar

Innan du tittar på teknikerna hjälper det att veta hur kompilatorn spårar potentiella överträdelser av null-tillstånd. När koden läss håller kompilatorn reda på varje uttrycks null-tillstånd: dess analys om huruvida uttrycket kan finnas null vid den tidpunkten i koden. Null-tillståndet är ett av två värden:

  • not-null – kompilatorn kan bevisa att uttrycket inte null finns här. Du kan använda den utan kontroll på ett säkert sätt.
  • maybe-null – kompilatorn kan inte utesluta null. Om du använder uttrycket utan att kontrollera det genereras en varning.

En variabels null-tillstånd ändras när kompilatorn följer koden. En metod som kan returnera null ger ett resultat som kanske är null . En if (x is not null)-kontroll snävar in x till icke-null inuti blocket if. Varningarna du ser är kompilatorn som anger att ett uttryck är i ett kanske null-tillstånd och att du är på väg att använda det som om det inte vore null. Varje teknik i resten av den här artikeln är ett annat sätt att ge kompilatorn den information den behöver för att säkerställa att ett uttryck inte är null innan du använder det.

Lägg till en null-kontroll

Den vanligaste varningen är möjlig avreferering av null. Kompilatorn spårade en variabels null-tillstånd till kanske null och såg variabeln användas utan kontroll:

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

Korrigeringen är vanligtvis en skyddsklausul. Ett vaktvillkor är en kontroll i början av en metod eller ett block som returnerar eller kastar ett undantag när ett indatavärde är ogiltigt. Endast den säkra vägen fortsätter. När kontrollen körs uppdaterar kompilatorn variabelns null-tillstånd till icke-null på den säkra sökvägen:

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;
}

Mönstermatchning (uttryck som is null eller is { } som testar formen på ett värde), ??och ??= inkluderar null-kontroller:

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;
}

Egenskapsmönstret { Length: > 0 } matchar endast när message inte är null och dess Length-egenskap är större än noll, så kompilatorn behandlar message som icke-null i blocket if. Ett enklare is not null test snävar in null-tillståndet på samma sätt utan att undersöka några egenskaper.

En djupgående genomgång av operatorerna finns i Null-operatorer.

Justera anteckningar

Kompilatorn varnar dig också när din kod tilldelar ett uttryck som kan vara null till en icke-nullbar variabel. Varningen innebär en av två saker:

  • Variabeln bör tillåta null-värden. I så fall lägger du till en ? i typen .
  • Uttrycket genererar aldrig ett null-värde. Kommentera API:et som skapade det.
public static void AssignmentWarning()
{
    // Warning CS8600: converting null literal or possible null value to non-nullable type.
    string name = Lookup("nobody");
    Console.WriteLine(name);
}

Om Lookup null returneras på ett legitimt sätt ändrar du anropswebbplatsen så att den accepterar det saknade värdet:

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

Om Lookup aldrig returnerar null ändrar du dess signatur så att den returnerar en icke-nullbar referenstyp. Scenarier där null-tillståndet för det returnerade värdet är beroende av indata, se följande avsnitt om null-analysattribut.

Använd endast operatorn ! null-forgiving när du kan garantera att ett värde inte är null men inte kan uttrycka den garantin i typsystemet. Varje ! är en plats där kompilatorn inte längre kan skydda dig, så lägg helst till en kontroll eller annotera käll-API:et.

Lägga till ett null-analysattribut

Ibland finns den rätta lösningen inte i anropet. En metods signatur avbildar inte relationen mellan dess indata och utdata tillräckligt exakt, och kompilatorn utfärdar varningar i annars säker kod:

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

Brödtexten IsPresent i bevisar att argumentet inte är null när metoden returnerar true, men signaturen säger inte det. Lägg till ett attribut för nullbar analys för att göra kontraktet till en del av API:et:

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

Vanliga attribut är:

Den fullständiga listan finns i statiska analysattribut för null-värden.

Initiera icke-nullbara medlemmar

En konstruktorvarning innebär att ett fält, en egenskap eller en autoegenskap som inte kan nulliseras (en egenskap som använder det kompilatorgenererade bakgrundsfältet, till exempel public string Name { get; set; }) avslutar konstruktorn utan att tilldelas ett icke-null-värde:

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

Du har flera sätt att hantera det. Välj den som bäst matchar designens avsikt.

Kräv värdet som ett konstruktorargument. Använd en primär konstruktor (parametrar som deklareras på själva typen, tillgängliga i hela brödtexten) eller en vanlig konstruktor som initierar egenskapen:

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

Gör egenskapen till required. Anroparen måste initiera den via en objektinitierare (syntaxen { Property = value } som följer new):

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

Initiera med ett standardvärde. När typen har ett meningsfullt tomt värde initierar du vid deklarationen:

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

Tip

Välj endast den här tekniken när typen har ett verkligt bra standardvärde: en som är en giltig, fullt fungerande instans som anropare kan använda. Exempel är tomma samlingar. Hitta inte på en sentinel (ett platshållarvärde som String.Empty, "N/A", "unknown" eller -1 som du behandlar som ”inget värde”) som ersättning för null: det tystar varningen, men varje anropare måste känna till och kontrollera efter sentinelvärdet, och typsystemet kan inte hjälpa. När det inte finns någon bra standard gör du egenskapen nullbar i stället.

Gör egenskapen null. När värdet verkligen saknas ändrar du typen till nullbar:

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

Om en hjälpmetod initierar medlemmen kommenterar du hjälpen med MemberNotNullAttribute så att kompilatorn kan kreditera anrop till den.

Verifiera projektinställningen

Nya C#-projekt aktiverar nullbara referenstyper som standard, så de flesta kod som du skriver eller läser har redan funktionen aktiverad. Du behöver vanligtvis inte konfigurera något. Om du är nyfiken på om ett projekt har aktiverats eller om du behöver ändra inställningen letar du efter elementet <Nullable>.csproji :

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

De vanliga värden som stöds är enable (standardvärdet för nya projekt) och disable. Om elementet saknas använder projektet det standardvärde som SDK:n och målramverket anger.

Om du bara behöver aktivera nullable för en del av en fil med #nullable-direktiv, eller använda de partiella lägena warnings och annotations när du migrerar en befintlig kodbas, kan du läsa Migreringsstrategier för nullable.

Vart nästa?

När en varning inte passar något av dessa mönster, visar referensartikeln Lös nullbara varningar tekniken för varje CS86xx-varning som kompilatorn genererar.

Om du vill planera en migrering som gradvis aktiverar nullbara referenstyper i en befintlig kodbas, se Strategier för migrering till nullbara referenstyper.