Lösa null-varningar

Den här artikeln beskriver följande kompilatorvarningar:

  • CS8597 - Genererat värde kan vara null.
  • CS8600 - Konvertera nullliteral eller möjligt null-värde till icke-nullbar typ.
  • CS8601 - Möjlig null-referenstilldelning.
  • CS8602 - Referens för en möjligen null-referens.
  • CS8603 - Möjlig nullreferensretur.
  • CS8604 - Möjligt null-referensargument för parameter.
  • CS8605 - Avboxa ett eventuellt null-värde.
  • CS8607 - Ett möjligt null-värde kanske inte används för en typ som är markerad med [NotNull] eller [DisallowNull]
  • CS8608 - Nullability of reference types in type matchar inte åsidosatt medlem.
  • CS8609 - Nullability för referenstyper i returtyp matchar inte åsidosatt medlem.
  • CS8610 - Nullability för referenstyper i typparametern matchar inte åsidosatt medlem.
  • CS8611 - Nullability för referenstyper i typparametern matchar inte partiell metoddeklaration.
  • CS8612 - Nullability of reference types in type matchar inte implicit implementerad medlem.
  • CS8613 - Nullability för referenstyper i returtyp matchar inte implicit implementerad medlem.
  • CS8614 - Nullability för referenstyper i typ av parameter matchar inte implicit implementerad medlem.
  • CS8615 - Nullability för referenstyper av typen matchar inte implementerad medlem.
  • CS8616 - Nullability för referenstyper i returtyp matchar inte implementerad medlem.
  • CS8617 - Nullability för referenstyper i typ av parameter matchar inte implementerad medlem.
  • CS8618 - Icke-nullbar variabel måste innehålla ett värde som inte är null när konstruktorn avslutas. Överväg att deklarera det som nullbart.
  • CS8619 - Nullability för referenstyper i värde matchar inte måltypen.
  • CS8620-argument - kan inte användas för parametern på grund av skillnader i referenstypernas nullbarhet.
  • CS8621 - Nullability för referenstyper i returtyp matchar inte måldelegaten (möjligen på grund av nullatribut).
  • CS8622 - Nullability för referenstyper i typ av parameter matchar inte måldelegaten (möjligen på grund av nullatribut).
  • CS8624-argumentet - kan inte användas som utdata på grund av skillnader i referenstypernas nullbarhet.
  • CS8625 - Det går inte att konvertera nullliteral till icke-nullbar referenstyp.
  • CS8629 - Nullable-värdetypen kan vara null.
  • CS8631 - Typen kan inte användas som typparameter i den generiska typen eller metoden. Ogiltighet av typen argument matchar inte villkorstypen.
  • CS8633 - Nullability i begränsningar för typparametern för metoden matchar inte begränsningarna för typparametern för gränssnittsmetoden. Överväg att använda en explicit gränssnittsimplementering i stället.
  • CS8634 - Typen kan inte användas som typparameter i den generiska typen eller metoden. Nullbarhet för typargument matchar inte villkoret "klass".
  • CS8643 - Nullability för referenstyper i explicit gränssnittsspecificerare matchar inte gränssnittet som implementeras av typen .
  • CS8644-typen - implementerar inte gränssnittsmedlem. Nullbarheten för referenstyper i gränssnittet som implementeras av bastypen matchar inte.
  • CS8645 - Member finns redan med i gränssnittslistan på typen med olika nullbarhet för referenstyper.
  • CS8655 - Switch-uttrycket hanterar inte några null-indata (det är inte uttömmande).
  • CS8667 - Partiella metoddeklarationer har inkonsekvent nullabilitet i begränsningar för typparameter.
  • CS8670 - Object or collection initializer dereferences implicit null member.
  • CS8714 - Typen kan inte användas som typparameter i den generiska typen eller metoden. Nullbarhet för typargument matchar inte villkoret "notnull".
  • CS8762-parametern - måste ha ett värde som inte är null när du avslutar.
  • CS8763 - En markerad [DoesNotReturn] metod bör inte returneras.
  • CS8764 - Nullability av returtyp matchar inte åsidosatt medlem (möjligen på grund av nullatribut).
  • CS8765 - Nullability av typen av parameter matchar inte åsidosatt medlem (möjligen på grund av nullatribut).
  • CS8766 - Nullability för referenstyper i returtypen matchar inte implicit implementerad medlem (möjligen på grund av nullatribut).
  • CS8767 - Nullability of reference types in type of parameter of doesn't match implicit implemented member (eventuellt på grund av nullability attributes).
  • CS8768 - Nullability för referenstyper i returtyp matchar inte implementerad medlem (möjligen på grund av nullatribut).
  • CS8769 - Nullability för referenstyper i typ av parameter matchar inte implementerad medlem (möjligen på grund av nullatribut).
  • CS8770-metoden - saknar [DoesNotReturn] anteckning för att matcha implementerad eller åsidosatt medlem.
  • CS8774 - Member måste ha ett värde som inte är null när du avslutar.
  • CS8776 - Member kan inte användas i det här attributet.
  • CS8775 - Member måste ha ett värde som inte är null när du avslutar.
  • CS8777-parametern - måste ha ett värde som inte är null när du avslutar.
  • CS8819 - Nullability för referenstyper i returtyp matchar inte partiell metoddeklaration.
  • CS8824-parametern - måste ha ett värde som inte är null när den avslutas eftersom parametern inte är null.
  • CS8825 - Return-värdet måste vara icke-null eftersom parametern inte är null.
  • CS8847 - Switch-uttrycket hanterar inte några null-indata (det är inte uttömmande). Ett mönster med en "when"-sats kan dock matcha det här värdet.

Syftet med null-varningar är att minimera risken för att programmet genererar en System.NullReferenceException när den körs. För att uppnå det här målet använder kompilatorn statisk analys och utfärdar varningar när koden har konstruktioner som kan leda till null-referensfel. Du ger kompilatorn information om dess statiska analys genom att tillämpa typanteckningar och attribut. Dessa anteckningar och attribut beskriver nullbarheten för argument, parametrar och medlemmar av dina typer. I den här artikeln lär du dig olika tekniker för att hantera de nullbara varningar som kompilatorn genererar från sin statiska analys. De tekniker som beskrivs här är för allmän C#-kod. Lär dig att arbeta med null-referenstyper och Entity Framework-kärnan i Arbeta med null-referenstyper.

Du kommer att hantera nästan alla varningar med någon av fyra tekniker:

  • Lägga till nödvändiga null-kontroller.
  • Lägga till ? eller ! null-anteckningar.
  • Lägga till attribut som beskriver null-semantik.
  • Initierar variabler korrekt.

Möjlig avreferering av null

Den här uppsättningen varningar varnar dig om att du avrefererar en variabel vars null-tillstånd kanske är null. Dessa varningar är:

  • CS8602 - Referens för en möjligen null-referens.
  • CS8670 - Object or collection initializer dereferences implicit null member.

Följande kod visar ett exempel på var och en av föregående varningar:

class Container
{
    public List<string>? States { get; set; }
}

internal void PossibleDereferenceNullExamples(string? message)
{
    Console.WriteLine(message.Length); // CS8602

    var c = new Container { States = { "Red", "Yellow", "Green" } }; // CS8670
}

I exemplet ovan beror varningen på att Container, c, kan ha ett null-värde för States egenskapen. Att tilldela nya tillstånd till en samling som kan vara null orsakar varningen.

Om du vill ta bort dessa varningar måste du lägga till kod för att ändra variabelns null-tillstånd till not-null innan du avrefererar den. Insamlingsinitierarens varning kan vara svårare att upptäcka. Kompilatorn identifierar att samlingen kanske-null när initieraren lägger till element i den.

I många fall kan du åtgärda dessa varningar genom att kontrollera att en variabel inte är null innan du avrefererar den. Tänk på följande som lägger till en null-kontroll innan du avrefererar parametern message :

void WriteMessageLength(string? message)
{
    if (message is not null)
    {
        Console.WriteLine(message.Length);
    }
    
}

I följande exempel initieras säkerhetskopieringslagringen States för och tar bort set accessorn. Användare av klassen kan ändra innehållet i samlingen och lagringen för samlingen är aldrig null:

class Container
{
    public List<string> States { get; } = new();
}

Andra instanser när du får dessa varningar kan vara falska positiva. Du kan ha en privat verktygsmetod som testar för null. Kompilatorn vet inte att metoden ger en null-kontroll. Tänk dig följande exempel som använder en privat verktygsmetod: IsNotNull

public void WriteMessage(string? message)
{
    if (IsNotNull(message))
        Console.WriteLine(message.Length);
}

Kompilatorn varnar för att du kan dereferencing null när du skriver egenskapen message.Length eftersom dess statiska analys avgör att message kan vara null. Du kanske vet att IsNotNull det ger en null-kontroll, och när den returnerar truebör null-tillståndetmessage för inte vara null. Du måste berätta dessa fakta för kompilatorn. Ett sätt är att använda operatorn null-förlåtande, !. Du kan ändra -instruktionen WriteLine så att den matchar följande kod:

Console.WriteLine(message!.Length);

Operatorn null forgiving gör uttrycket inte null även om det kanske var null utan tillämpad ! . I det här exemplet är en bättre lösning att lägga till ett attribut i signaturen IsNotNullför :

private static bool IsNotNull([NotNullWhen(true)] object? obj) => obj != null;

System.Diagnostics.CodeAnalysis.NotNullWhenAttribute Informerar kompilatorn om att argumentet som används för parametern obj inte är null när metoden returnerar true. När metoden returnerar falsehar argumentet samma null-tillstånd som det hade innan metoden anropades.

Dricks

Det finns en omfattande uppsättning attribut som du kan använda för att beskriva hur dina metoder och egenskaper påverkar null-tillstånd. Du kan lära dig mer om dem i språkreferensartikeln om statiska analysattribut som kan användas som null.

Om du åtgärdar en varning för att avreferera en variabel som kanske är null finns det någon av tre tekniker:

  • Lägg till en null-kontroll som saknas.
  • Lägg till null-analysattribut på API:er för att påverka kompilatorns statiska analys med null-tillstånd . Dessa attribut informerar kompilatorn när ett returvärde eller argument ska vara kanske null eller inte null efter att metoden anropats.
  • Använd null-förlåtande operatorn ! för uttrycket för att tvinga tillståndet till not-null.

Möjlig null tilldelad till en icke-inullerbar referens

Den här uppsättningen med varningar varnar dig om att du tilldelar en variabel vars typ inte kanulleras till ett uttryck vars null-tillstånd kanske är null. Dessa varningar är:

  • CS8597 - Genererat värde kan vara null.
  • CS8600 - Konvertera nullliteral eller möjligt null-värde till icke-nullbar typ.
  • CS8601 - Möjlig null-referenstilldelning.
  • CS8603 - Möjlig nullreferensretur.
  • CS8604 - Möjligt null-referensargument för parameter.
  • CS8605 - Avboxa ett eventuellt null-värde.
  • CS8625 - Det går inte att konvertera nullliteral till icke-nullbar referenstyp.
  • CS8629 - Nullable-värdetypen kan vara null.

Kompilatorn genererar dessa varningar när du försöker tilldela ett uttryck som kanske är null till en variabel som inte kan inaktiveras. Till exempel:

string? TryGetMessage(int id) => "";

string msg = TryGetMessage(42);  // Possible null assignment.

De olika varningarna visar information om koden, till exempel tilldelning, avboxningstilldelning, returinstruktioner, argument till metoder och utsända uttryck.

Du kan vidta någon av tre åtgärder för att åtgärda dessa varningar. En är att lägga till anteckningen ? för att göra variabeln till en nullbar referenstyp. Ändringen kan orsaka andra varningar. Om du ändrar en variabel från en icke-nullbar referens till en nullbar referens ändras standardvärdet null-state från not-null till maybe-null. Kompilatorns statiska analys kan hitta instanser där du avreferera en variabel som kanske är null.

De andra åtgärderna instruerar kompilatorn att tilldelningens högra sida inte är null. Uttrycket till höger kan vara null-markerat före tilldelningen, enligt följande exempel:

string notNullMsg = TryGetMessage(42) ?? "Unknown message id: 42";

Föregående exempel visar tilldelning av returvärdet för en metod. Du kan kommentera metoden (eller egenskapen) för att ange när en metod returnerar ett värde som inte är null. Anger System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute ofta att ett returvärde inte är null när ett indataargument inte är null. Ett annat alternativ är att lägga till null-förlåtande operatorn ! till höger:

string msg = TryGetMessage(42)!;

Om du åtgärdar en varning för att tilldela ett uttryck som kanske är null till en variabel som inte är null , ingår någon av fyra tekniker:

  • Ändra den vänstra sidan av tilldelningen till en nullbar typ. Den här åtgärden kan introducera nya varningar när du avrefererar variabeln.
  • Ange en null-kontroll före tilldelningen.
  • Kommentera API:et som genererar den högra sidan av tilldelningen.
  • Lägg till operatorn null-förlåtande till höger i tilldelningen.

Icke-inullerbar referens har inte initierats

Den här uppsättningen varningar varnar dig om att du tilldelar en variabel vars typ inte kan nulliseras till ett uttryck vars null-tillstånd kanske är null. Dessa varningar är:

  • CS8618 - Icke-nullbar variabel måste innehålla ett värde som inte är null när konstruktorn avslutas. Överväg att deklarera det som nullbart.
  • CS8762-parametern - måste ha ett värde som inte är null när du avslutar.

Se följande klass som ett exempel:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Varken FirstName eller LastName garanteras initieras. Om den här koden är ny kan du överväga att ändra det offentliga gränssnittet. Exemplet ovan kan uppdateras på följande sätt:

public class Person
{
    public Person(string first, string last)
    {
        FirstName = first;
        LastName = last;
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Om du behöver skapa ett Person objekt innan du anger namnet kan du initiera egenskaperna med ett standardvärde som inte är null:

public class Person
{
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
}

Ett annat alternativ kan vara att ändra dessa medlemmar till null-referenstyper. Klassen Person kan definieras på följande sätt om null ska tillåtas för namnet:

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

Befintlig kod kan kräva andra ändringar för att informera kompilatorn om null-semantiken för dessa medlemmar. Du kan ha skapat flera konstruktorer och din klass kan ha en privat hjälpmetod som initierar en eller flera medlemmar. Du kan flytta initieringskoden till en enda konstruktor och se till att alla konstruktorer anropar den med den gemensamma initieringskoden. Eller så kan du använda attributen System.Diagnostics.CodeAnalysis.MemberNotNullAttribute och System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute . Dessa attribut informerar kompilatorn om att en medlem inte är null efter att metoden har anropats. Följande kod visar ett exempel på var och en. Klassen Person använder en vanlig konstruktor som anropas av alla andra konstruktorer. Klassen Student har en hjälpmetod kommenterad med System.Diagnostics.CodeAnalysis.MemberNotNullAttribute attributet:


using System.Diagnostics.CodeAnalysis;

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public Person() : this("John", "Doe") { }
}

public class Student : Person
{
    public string Major { get; set; }

    public Student(string firstName, string lastName, string major)
        : base(firstName, lastName)
    {
        SetMajor(major);
    }

    public Student(string firstName, string lastName) :
        base(firstName, lastName)
    {
        SetMajor();
    }

    public Student()
    {
        SetMajor();
    }

    [MemberNotNull(nameof(Major))]
    private void SetMajor(string? major = default)
    {
        Major = major ?? "Undeclared";
    }
}

Slutligen kan du använda null-förlåtande operatorn för att ange att en medlem har initierats i annan kod. Tänk dig till exempel följande klasser som representerar en Entity Framework Core-modell:

public class TodoItem
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

public class TodoContext : DbContext
{
    public TodoContext(DbContextOptions<TodoContext> options)
        : base(options)
    {
    }

    public DbSet<TodoItem> TodoItems { get; set; } = null!;
}

Egenskapen DbSet initieras till null!. Det talar om för kompilatorn att egenskapen är inställd på ett värde som inte är null . I själva verket utför basen DbContext initieringen av uppsättningen. Kompilatorns statiska analys tar inte upp det. Mer information om hur du arbetar med null-referenstyper och Entity Framework Core finns i artikeln arbeta med nullbara referenstyper i EF Core.

Att åtgärda en varning för att inte initiera en icke-inullerbar medlem omfattar någon av fyra tekniker:

  • Ändra konstruktorerna eller fältinitierarna så att alla icke-inullerbara medlemmar initieras.
  • Ändra en eller flera medlemmar till null-typer.
  • Kommentera eventuella hjälpmetoder för att ange vilka medlemmar som har tilldelats.
  • Lägg till en initialiserare för null! att indikera att medlemmen initieras i annan kod.

Matchningsfel i null-deklaration

Många varningar indikerar ogiltighetsmatchningar mellan signaturer för metoder, ombud eller typparametrar.

  • CS8608 - Nullability of reference types in type matchar inte åsidosatt medlem.
  • CS8609 - Nullability för referenstyper i returtyp matchar inte åsidosatt medlem.
  • CS8610 - Nullability för referenstyper i typparametern matchar inte åsidosatt medlem.
  • CS8611 - Nullability för referenstyper i typparametern matchar inte partiell metoddeklaration.
  • CS8612 - Nullability of reference types in type matchar inte implicit implementerad medlem.
  • CS8613 - Nullability för referenstyper i returtyp matchar inte implicit implementerad medlem.
  • CS8614 - Nullability för referenstyper i typ av parameter matchar inte implicit implementerad medlem.
  • CS8615 - Nullability för referenstyper av typen matchar inte implementerad medlem.
  • CS8616 - Nullability för referenstyper i returtyp matchar inte implementerad medlem.
  • CS8617 - Nullability för referenstyper i typ av parameter matchar inte implementerad medlem.
  • CS8619 - Nullability för referenstyper i värde matchar inte måltypen.
  • CS8620-argument - kan inte användas för parametern på grund av skillnader i referenstypernas nullbarhet.
  • CS8621 - Nullability för referenstyper i returtyp matchar inte måldelegaten (möjligen på grund av nullatribut).
  • CS8622 - Nullability för referenstyper i typ av parameter matchar inte måldelegaten (möjligen på grund av nullatribut).
  • CS8624-argumentet - kan inte användas som utdata på grund av skillnader i referenstypernas nullbarhet.
  • CS8631 - Typen kan inte användas som typparameter i den generiska typen eller metoden. Ogiltighet av typen argument matchar inte villkorstypen.
  • CS8633 - Nullability i begränsningar för typparametern för metoden matchar inte begränsningarna för typparametern för gränssnittsmetoden. Överväg att använda en explicit gränssnittsimplementering i stället.
  • CS8634 - Typen kan inte användas som typparameter i den generiska typen eller metoden. Nullbarhet för typargument matchar inte villkoret "klass".
  • CS8643 - Nullability för referenstyper i explicit gränssnittsspecificerare matchar inte gränssnittet som implementeras av typen .
  • CS8644-typen - implementerar inte gränssnittsmedlem. Nullbarheten för referenstyper i gränssnittet som implementeras av bastypen matchar inte.
  • CS8645 - Member finns redan med i gränssnittslistan på typen med olika nullbarhet för referenstyper.
  • CS8667 - Partiella metoddeklarationer har inkonsekvent nullabilitet i begränsningar för typparameter.
  • CS8714 - Typen kan inte användas som typparameter i den generiska typen eller metoden. Nullbarhet för typargument matchar inte villkoret "notnull".
  • CS8764 - Nullability av returtyp matchar inte åsidosatt medlem (möjligen på grund av nullatribut).
  • CS8765 - Nullability av typen av parameter matchar inte åsidosatt medlem (möjligen på grund av nullatribut).
  • CS8766 - Nullability för referenstyper i returtypen matchar inte implicit implementerad medlem (möjligen på grund av nullatribut).
  • CS8767 - Nullability of reference types in type of parameter of doesn't match implicit implemented member (eventuellt på grund av nullability attributes).
  • CS8768 - Nullability för referenstyper i returtyp matchar inte implementerad medlem (möjligen på grund av nullatribut).
  • CS8769 - Nullability för referenstyper i typ av parameter matchar inte implementerad medlem (möjligen på grund av nullatribut).
  • CS8819 - Nullability för referenstyper i returtyp matchar inte partiell metoddeklaration.

Följande kod visar CS8764:

public class B
{
    public virtual string GetMessage(string id) => string.Empty;
}
public class D : B
{
    public override string? GetMessage(string? id) => default;
}

Föregående exempel visar en virtual metod i en basklass och en override med annan nullabilitet. Basklassen returnerar en icke-nullbar sträng, men den härledda klassen returnerar en nullbar sträng. string Om och string? är omvända tillåts det eftersom den härledda klassen är mer restriktiv. På samma sätt bör parameterdeklarationer matcha. Parametrar i åsidosättningsmetoden kan tillåta null även om basklassen inte gör det.

Andra situationer kan generera dessa varningar. Du kan ha ett matchningsfel i en gränssnittsmetoddeklaration och implementeringen av den metoden. Eller en ombudstyp och uttrycket för ombudet kan skilja sig åt. En typparameter och typargumentet kan skilja sig åt i nullabilitet.

Om du vill åtgärda dessa varningar uppdaterar du lämplig deklaration.

Koden matchar inte attributdeklarationen

Föregående avsnitt har diskuterat hur du kan använda attribut för statisk analys som kan vara null för att informera kompilatorn om null-semantiken i koden. Kompilatorn varnar dig om koden inte följer löftena för det attributet:

  • CS8607 - Ett möjligt null-värde kanske inte används för en typ som är markerad med [NotNull] eller [DisallowNull]
  • CS8763 - En markerad [DoesNotReturn] metod bör inte returneras.
  • CS8770-metoden - saknar [DoesNotReturn] anteckning för att matcha implementerad eller åsidosatt medlem.
  • CS8774 - Member måste ha ett värde som inte är null när du avslutar.
  • CS8775 - Member måste ha ett värde som inte är null när du avslutar.
  • CS8776 - Member kan inte användas i det här attributet.
  • CS8777-parametern - måste ha ett värde som inte är null när du avslutar.
  • CS8824-parametern - måste ha ett värde som inte är null när den avslutas eftersom parametern inte är null.
  • CS8825 - Return-värdet måste vara icke-null eftersom parametern inte är null.

Tänk på följande metod:

public bool TryGetMessage(int id, [NotNullWhen(true)] out string? message)
{
    message = null;
    return true;

}

Kompilatorn skapar en varning eftersom parametern message har tilldelats nulloch metoden returnerar true. Attributet NotNullWhen anger att det inte ska ske.

För att åtgärda dessa varningar uppdaterar du koden så att den matchar förväntningarna på de attribut som du har tillämpat. Du kan ändra attributen eller algoritmen.

Fullständigt växeluttryck

Switch-uttryck måste vara uttömmande, vilket innebär att alla indatavärden måste hanteras. Även för icke-nullbara referenstyper null måste värdet redovisas. Kompilatorn utfärdar varningar när null-värdet inte hanteras:

  • CS8655 - Switch-uttrycket hanterar inte några null-indata (det är inte uttömmande).
  • CS8847 - Switch-uttrycket hanterar inte några null-indata (det är inte uttömmande). Ett mönster med en "when"-sats kan dock matcha det här värdet.

Följande exempelkod visar det här villkoret:

int AsScale(string status) =>
    status switch
    {
        "Red" => 0,
        "Yellow" => 5,
        "Green" => 10,
        { } => -1
    };

Indatauttrycket är , stringinte en string?. Kompilatorn genererar fortfarande den här varningen. Mönstret { } hanterar alla icke-null-värden, men matchar nullinte . För att åtgärda dessa fel kan du antingen lägga till ett explicit null ärende eller ersätta { } med _ mönstret (ignorera). Mönstret ignorera matchar null och andra värden.