Lerntechniken zum Auflösen von Nullable-Warnungen

Der Zweck von nullablen Referenztypen besteht darin, die Wahrscheinlichkeit zu minimieren, dass Ihre Anwendung eine System.NullReferenceException Ausführung auslöst. Um dieses Ziel zu erreichen, verwendet der Compiler statische Analyse und Probleme bei Konstrukten, die zu Nullreferenzausnahmen führen können. Sie stellen den Compiler informationen zur statischen Analyse bereit, indem Sie Typanmerkungen und Attribute anwenden. Diese Anmerkungen und Attribute beschreiben die Nullbarkeit von Argumenten, Parametern und Mitgliedern Ihrer Typen. In diesem Artikel lernen Sie verschiedene Techniken kennen, um die nullablen Warnungen zu beheben, die der Compiler aus seiner statischen Analyse generiert. Die hier beschriebenen Techniken gelten für den allgemeinen C#-Code. Hier erfahren Sie, wie Sie mit nullablen Referenztypen und Entity Framework-Kern in " Arbeiten mit nullablen Referenztypen" arbeiten.

Sie werden fast alle Warnungen mit einer der vier Techniken adressieren:

  • Hinzufügen erforderlicher NULL-Überprüfungen.
  • Hinzufügen ? oder ! Nullablenanmerkungen.
  • Hinzufügen von Attributen, die null Semantik beschreiben.
  • Initialisieren von Variablen ordnungsgemäß.

Mögliche Ableitung von Null

Eine Reihe von Warnungen benachrichtigt Sie, dass Sie eine Variable ableiten, deren NULL-Zustand möglicherweise null ist. Ein Beispiel kann sein:

string message = null;
Console.WriteLine(message.Length);

Um diese Warnungen zu entfernen, müssen Sie Code hinzufügen, um den null-Zustand dieser Variable nicht zu null zu ändern, bevor Sie ihn ableiten.

In vielen Fällen können Sie diese Warnungen beheben, indem Sie überprüfen, ob eine Variable nicht null ist, bevor Sie sie ableiten. Beispielsweise könnte das obige Beispiel wie folgt neu geschrieben werden:

string message = null;
if (message is not null)
{
    Console.WriteLine(message.Length);
}

Wenn Ihr Code eine Warnung generiert, dass es möglicherweise einen Null-Verweis ableiten kann, stellen Sie sicher, dass Sie eine Nullprüfung durchgeführt haben. Wenn Sie keines haben, fügen Sie einen hinzu. Die Compilerwarnung hat Ihnen bei der Behebung eines möglichen Fehlers geholfen.

Andere Instanzen, wenn Sie diese Warnungen erhalten, sind möglicherweise falsch positiv. Möglicherweise verfügen Sie über eine private Hilfsmethode, die auf Null getestet wird. Der Compiler weiß nicht, dass die Methode eine Nullprüfung bereitstellt. Betrachten Sie das folgende Beispiel, das eine private Hilfsmethode verwendet, IsNotNull:

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

Der Compiler warnt, dass Sie möglicherweise null ableiten, wenn Sie die Eigenschaft message.Length schreiben, da die statische Analyse bestimmt, dass dies message möglicherweise nullsein kann. Möglicherweise wissen Sie, dass eine Nullprüfung bereitgestellt wird, und wenn IsNotNull sie truezurückgegeben wird, sollte der Null-Zustandmessagenicht null sein. Sie müssen den Compiler über diese Fakten informieren. Eine Möglichkeit besteht darin, den Null-Verzeihungsoperator zu verwenden. ! Sie können die WriteLine Anweisung ändern, um dem folgenden Code zu entsprechen:

Console.WriteLine(message!.Length);

Der Null-Operator macht den Ausdruck nicht null , auch wenn es vielleicht null ohne die ! Anwendung war. In diesem Beispiel ist eine bessere Lösung das Hinzufügen eines Attributs zur Signatur von IsNotNull:

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

Der System.Diagnostics.CodeAnalysis.NotNullWhenAttribute Compiler informiert den Compiler, dass das für den obj Parameter verwendete Argument nicht null ist, wenn die Methode zurückgibt true. Wenn die Methode zurückgegeben falsewird, weist das Argument denselben NULL-Zustand auf, den es vor dem Aufruf der Methode hatte.

Tipp

Es gibt einen umfangreichen Satz von Attributen, mit denen Sie beschreiben können, wie Sich Ihre Methoden und Eigenschaften auf null-Zustand auswirken. Sie können sie im Sprachreferenzartikel zu Nullable statischen Analyseattributen erfahren.

Das Beheben einer Warnung für die Ableitung einer möglicherweise null-Variablen umfasst eine der drei Techniken:

  • Fügen Sie eine fehlende Nullprüfung hinzu.
  • Fügen Sie NULL-Analyseattribute für APIs hinzu, um die statische Analyse des Compilers zu beeinflussen. Diese Attribute informieren den Compiler, wenn ein Rückgabewert oder ein Argument möglicherweise null oder nicht null sein sollte, nachdem die Methode aufgerufen wurde.
  • Wenden Sie den Null-Zuweisen-Operator ! auf den Ausdruck an, um den Zustand nicht null zu erzwingen.

Mögliche Null, die einem nicht ullierbaren Verweis zugewiesen ist

Der Compiler gibt diese Warnungen aus, wenn Sie versuchen, einen Ausdruck zuzuweisen, der möglicherweise null ist, einer Variable, die nicht ullierbar ist. Beispiel:

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

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

Sie können eine der drei Aktionen ausführen, um diese Warnungen zu beheben. Eins besteht darin, die Anmerkung hinzuzufügen, um die ? Variable zu einem nullablen Referenztyp zu machen. Diese Änderung kann zu anderen Warnungen führen. Das Ändern einer Variable aus einem nicht nullablen Verweis auf einen nullablen Verweis ändert den Standardmäßigen NULL-Zustand von nicht null auf möglicherweise null. Die statische Analyse des Compilers kann Instanzen finden, in denen Sie eine Variable ableiten, die vielleicht null ist.

Die anderen Aktionen weisen den Compiler an, dass die rechte Seite der Zuordnung nicht null ist. Der Ausdruck auf der rechten Seite könnte vor der Zuordnung null überprüft werden, wie im folgenden Beispiel gezeigt:

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

In den vorherigen Beispielen wird die Zuordnung zum Rückgabewert einer Methode veranschaulicht. Sie können die Methode (oder Eigenschaft) anmerken, um anzugeben, wann eine Methode einen Nicht-NULL-Wert zurückgibt. Häufig System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute wird angegeben, dass ein Rückgabewert nicht null ist, wenn ein Eingabeargument nicht null ist. Eine weitere Alternative besteht darin, ! den Null-Verzeihungsoperator rechts hinzuzufügen:

string msg = TryGetMessage(42)!;

Das Beheben einer Warnung zum Zuweisen eines möglicherweise null-Ausdrucks zu einer nicht null Variablen umfasst eine der vier Techniken:

  • Ändern Sie die linke Seite der Zuordnung in einen nullablen Typ. Diese Aktion kann neue Warnungen einführen, wenn Sie diese Variable ableiten.
  • Stellen Sie vor der Zuordnung eine Nullprüfung bereit.
  • Annotieren Sie die API, die die rechte Seite der Zuordnung erzeugt.
  • Fügen Sie den Null-Verzeihoperator zur rechten Seite der Zuordnung hinzu.

Nicht ullierbare Referenz nicht initialisiert

Andere Warnungen werden generiert, wenn eine nichtullierbare Referenzvariablen beim Deklarierten oder in einem Konstruktor nicht initialisiert wird. Betrachten Sie die folgende Klasse als Beispiel:

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

Weder FirstNameLastName noch werden garantiert initialisiert. Wenn dieser Code neu ist, sollten Sie die öffentliche Schnittstelle ändern. Das obige Beispiel könnte wie folgt aktualisiert werden:

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

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

Wenn Sie ein Objekt erstellen müssen, bevor Sie den Namen festlegen, können Sie die Eigenschaften mithilfe eines Person Standardwerts ohne Null initialisieren:

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

Eine andere Alternative kann es sein, diese Elemente in nullable Referenztypen zu ändern. Die Person Klasse könnte wie folgt definiert werden, wenn null für den Namen zulässig sein sollte:

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

Mit vorhandenem Code können andere Änderungen erforderlich sein, um den Compiler über die Null-Semantik für diese Elemente zu informieren. Möglicherweise haben Sie mehrere Konstruktoren erstellt, und Ihre Klasse verfügt möglicherweise über eine private Hilfsmethode, die mindestens ein Element initialisiert. Sie können den Initialisierungscode in einen einzelnen Konstruktor verschieben und sicherstellen, dass alle Konstruktoren mit dem allgemeinen Initialisierungscode aufgerufen werden. Sie können auch die attribute System.Diagnostics.CodeAnalysis.MemberNotNullAttributeSystem.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute verwenden. Diese Attribute informieren den Compiler, dass ein Element nicht null ist, nachdem die Methode aufgerufen wurde. Im folgenden Code ist ein Beispiel für jede Methode dargestellt. Die Person Klasse verwendet einen gemeinsamen Konstruktor, der von allen anderen Konstruktoren aufgerufen wird. Die Student Klasse verfügt über eine Hilfsmethode, die mit dem System.Diagnostics.CodeAnalysis.MemberNotNullAttribute Attribut versehen ist:


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

Schließlich können Sie den Null-Verzeihungsoperator verwenden, um anzugeben, dass ein Element im anderen Code initialisiert wird. Berücksichtigen Sie für ein anderes Beispiel die folgenden Klassen, die ein Entity Framework Core-Modell darstellen:

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

Die DbSet-Eigenschaft wird mit null! initialisiert. Das teilt dem Compiler mit, dass die Eigenschaft auf einen Nicht-Null-Wert festgelegt ist. Tatsächlich führt die Basis DbContext die Initialisierung des Satzs aus. Die statische Analyse des Compilers nimmt dies nicht auf. Weitere Informationen zum Arbeiten mit nullablen Referenztypen und Entity Framework Core finden Sie im Artikel zum Arbeiten mit Nullable-Referenztypen in EF Core.

Das Beheben einer Warnung zum Initialisieren eines nicht ullierbaren Members umfasst eine der vier Techniken:

  • Ändern Sie die Konstruktoren oder Feld-Initializer, um sicherzustellen, dass alle nicht ullierbaren Elemente initialisiert werden.
  • Ändern Sie mindestens ein Element, um nullable Typen zu sein.
  • Annotieren Sie alle Hilfsmethoden, um anzugeben, welche Elemente zugewiesen sind.
  • Fügen Sie einen Initializer hinzu, null! um anzugeben, dass das Element im anderen Code initialisiert wird.

Falsche Übereinstimmung in der Nullbarkeitsdeklaration

Andere Warnungen weisen auf Nullbarkeitsübereinstimmungen zwischen Signaturen für Methoden, Stellvertretungen oder Typparameter hin. Beispiel:

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

Im vorhergehenden Beispiel wird eine virtual Methode in einer Basisklasse und eine override mit unterschiedlicher Nullbarkeit dargestellt. Die Basisklasse gibt eine nicht nullable Zeichenfolge zurück, aber die abgeleitete Klasse gibt eine nullable Zeichenfolge zurück. Wenn dies stringstring? umgekehrt ist, wäre es zulässig, da die abgeleitete Klasse restriktiver ist. Ebenso sollten Parameterdeklarationen übereinstimmen. Parameter in der Außerkraftsetzungsmethode können null zulassen, auch wenn die Basisklasse nicht.

Andere Situationen können diese Warnungen generieren. Möglicherweise haben Sie eine Übereinstimmung in einer Schnittstellenmethodendeklaration und der Implementierung dieser Methode. Oder ein Stellvertretungstyp und der Ausdruck für diese Stellvertretung können unterschiedlich sein. Ein Typparameter und das Typargument können sich in der Nullierbarkeit unterscheiden.

Um diese Warnungen zu beheben, aktualisieren Sie die entsprechende Deklaration.

Code stimmt nicht mit der Attributdeklaration überein.

In den vorherigen Abschnitten wurde erläutert, wie Sie Attribute für eine nullfähige statische Analyse verwenden können, um den Compiler über die Nullsemantik Ihres Codes zu informieren. Der Compiler warnt Sie, wenn der Code nicht den Versprechen dieses Attributs entspricht. Sehen Sie sich die folgende Methode an:

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

}

Der Compiler erzeugt eine Warnung, da der message Parameter zugewiesen null ist und die Methode zurückgegeben truewird. Das NotNullWhen Attribut gibt an, dass dies nicht geschehen sollte.

Um diese Warnungen zu beheben, aktualisieren Sie Ihren Code so, dass sie den Erwartungen der attribute entspricht, die Sie angewendet haben. Sie können die Attribute oder den Algorithmus ändern.