Freigeben über


Nullwerte zulassende Verweistypen

In einem Nullwerte zulassenden oblivious-Kontext lassen alle Verweistypen Nullwerte zu. Als Nullable-Verweistypen wird eine Reihe von Features bezeichnet, die in einem Nullable-fähigen Kontext aktiviert werden und die die Wahrscheinlichkeit minimieren, dass Ihr Code zur Laufzeit System.NullReferenceException auslöst. Nullwerte zulassende Verweistypen enthalten drei Features, mit denen Sie diese Ausnahmen vermeiden können (einschließlich der Möglichkeit, einen Verweistyp explizit als Nullwerte zulassend zu kennzeichnen):

  • Verbesserte statische Flowanalyse, die bestimmt, ob eine Variable null sein kann, bevor sie dereferenziert wird.
  • Attribute, die APIs mit Anmerkungen kommentieren, sodass die Flussanalyse den NULL-Status bestimmt.
  • Variablenanmerkungen, die Entwickler verwenden, um den beabsichtigten NULL-Zustand für eine Variable explizit zu deklarieren.

Der Compiler verfolgt den NULL-Zustand jedes Ausdrucks im Code zur Kompilierungszeit. Der NULL-Zustand weist einen von drei Werten auf:

  • not-null: Der Ausdruck ist bekannt und nicht null.
  • maybe-null: Der Ausdruck könnte null sein.
  • oblivious: Der Compiler kann den Null-Zustand des Ausdrucks nicht bestimmen.

Variablenanmerkungen bestimmen die NULL-Zulässigkeit einer Verweistypvariable:

  • non-nullable: Wenn Sie der Variablen einen null-Wert oder einen mybe-null-Ausdruck zuweisen, gibt der Compiler eine Warnung aus. Variablen, die non-nullable sind, haben einen Standard-NULL-Zustand not-null.
  • nullable: Sie können der Variablen einen null-Wert oder einen mybe-null-Ausdruck zuweisen. Wenn der NULL-Zustand der Variablen maybe-nullist, gibt der Compiler eine Warnung aus, wenn Sie die Variable dereferenzieren. Der Standard-NULL-Zustand für die Variable ist maybe-null.
  • oblivious: Sie können der Variablen einen null-Wert oder einen maybe-null-Ausdruck zuweisen. Der Compiler gibt keine Warnungen aus, wenn Sie die Variable dereferenzieren oder wenn Sie der Variablen einen maybe-null-Ausdruck zuweisen.

Der NULL-Zustand oblivious und die NULL-Zulässigkeit oblivious stimmen mit dem Verhalten überein, bevor Nullwerte zulassende Verweistypen eingeführt wurden. Diese Werte sind während der Migration nützlich oder wenn Ihre App eine Bibliothek verwendet, für die Nullwerte zulassenden-Verweistypen nicht aktiviert sind.

Nullzustandsanalyse und Variablenanmerkungen sind für vorhandene Projekte standardmäßig deaktiviert. Dies bedeutet, dass alle Verweistypen weiterhin Nullwerte zulassen. Ab .NET 6 sind sie standardmäßig für neue Projekte aktiviert. Informationen zum Aktivieren dieser Features durch Deklarieren eines Nullable-Anmerkungskontexts finden Sie unter Nullable-Kontexte.

Im restlichen Teil dieses Artikels wird beschrieben, wie diese drei Featurebereiche funktionieren, um Warnungen zu generieren, wenn in Ihrem Code möglicherweise die Dereferenzierung eines null-Werts erfolgt. Das Dereferenzieren einer Variablen bedeutet, mithilfe des .-Operators (Punkt) auf einen ihrer Member zuzugreifen, wie im folgenden Beispiel gezeigt:

string message = "Hello, World!";
int length = message.Length; // dereferencing "message"

Wenn Sie eine Variable dereferenzieren, deren Wert null ist, löst die Laufzeit eine System.NullReferenceException aus.

Ebenso können Warnungen erzeugt werden, wenn [] die Notation verwendet wird, um auf ein Element eines Objekts zuzugreifen, wenn das Objekt lautet null:

using System;

public class Collection<T>
{
    private T[] array = new T[100];
    public T this[int index]
    {
        get => array[index];
        set => array[index] = value;
    }
}

public static void Main()
{
    Collection<int> c = default;
    c[10] = 1;    // CS8602: Possible derefence of null
}

Behandelte Themen:

  • Die NULL-Zustand-Analyse des Compilers: wie der Compiler bestimmt, ob ein Ausdruck not-null oder maybe-null ist.
  • Attribute, die auf APIs angewendet werden, die mehr Kontext für die NULL-Zustand-Analyse des Compilers bereitstellen.
  • Nullwerte zulassende Variablenanmerkungen, die Informationen zu Ihrer Absicht für Variablen bereitstellen. Anmerkungen sind nützlich für Felder, um den Standard-NULL-Zustand am Anfang der Membermethoden festzulegen.
  • Die Regeln für generische Typargumente. Neue Einschränkungen wurden hinzugefügt, da Typparameter Verweistypen oder Werttypen sein können. Das Suffix ? wird für Nullwert zulassende Werttypen und Nullwert zulassende Verweistypen unterschiedlich implementiert.
  • Nullwerte zulassende Kontexte helfen Ihnen beim Migrieren großer Projekte. Sie können während der Migration Nullwerte zulassende Kontexte oder Warnungen in Teilen Ihrer App aktivieren. Nachdem Sie sich mit weiteren Warnungen beschäftigt haben, können Sie Nullwerte zulassende Verweistypen für das gesamte Projekt aktivieren.

Schließlich erfahren Sie mehr über bekannte Fallstricke für die NULL-Zustand-Analyse in struct-Typen und -Arrays.

Sie können diese Konzepte auch in unserem Lernmodul zur Nullsicherheit in C# erkunden.

NULL-Zustand-Analyse

Wenn Nullwerte zulassende Verweistypen aktiviert sind, verfolgt die NULL-Zustand-Analyse den NULL-Zustand von Verweisen. Ein Ausdruck ist entweder not null oder maybe null. Der Compiler bestimmt auf zwei Arten, dass eine Variable nicht not-null ist:

  1. Die Variable wurde einem Wert zugewiesen, der bekanntermaßen not-null ist.
  2. Die Variable wurde mit null überprüft und seit dieser Überprüfung nicht mehr geändert.

Wenn Nullwerte zulassende Verweistypen nicht aktiviert sind, weisen alle Ausdrücke den NULL-Zustand oblivious auf. Im restlichen Abschnitt wird das Verhalten beschrieben, wenn Nullwerte zulassende Verweistypen aktiviert sind.

Jede Variable, die der Compiler nicht als not-null bestimmt hat, wird als maybe-null betrachtet. Die Analyse liefert Warnungen in Situationen, in denen Sie einen null-Wert versehentlich dereferenzieren könnten. Der Compiler generiert Warnungen basierend auf dem NULL-Status.

  • Wenn eine Variable not-null ist, kann diese Variable sicher dereferenziert werden.
  • Wenn eine Variable maybe-null ist, muss diese Variable überprüft werden, um sicherzustellen, dass sie vor der Deferenzierung nicht null ist.

Betrachten Sie das folgenden Beispiel:

string message = null;

// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");

var originalMessage = message;
message = "Hello, World!";

// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");

// warning!
Console.WriteLine(originalMessage.Length);

Im vorherigen Beispiel bestimmt der Compiler, dass messagemaybe-null ist, wenn die erste Meldung ausgegeben wird. Für die zweite Meldung wird keine Warnung angezeigt. Die letzte Codezeile generiert eine Warnung, weil originalMessage möglicherweise NULL ist. Das folgende Beispiel zeigt eine praktischere Verwendung, um eine Struktur von Knoten bis zum Stamm zu durchlaufen und jeden Knoten während des Durchlaufs zu verarbeiten:

void FindRoot(Node node, Action<Node> processNode)
{
    for (var current = node; current != null; current = current.Parent)
    {
        processNode(current);
    }
}

Der Code oben generiert keine Warnungen zum Dereferenzieren der Variablen current. Die statische Analyse bestimmt, dass current nie dereferenziert wird, wenn der Wert maybe-null ist. Die Variable current wird anhand von null überprüft, bevor auf current.Parent zugegriffen und current an die Aktion ProcessNode übergeben wird. Die vorherigen Beispiele zeigen, wie der Compiler den NULL-Status für lokale Variablen bestimmt, wenn initialisiert, zugewiesen oder mit null verglichen wird.

Die NULL-Zustand-Analyse führt keine Ablaufverfolgung in aufgerufene Methoden durch. Daher generieren Felder, die in einer gemeinsamen, von allen Konstruktoren aufgerufenen Hilfsmethode initialisiert werden, eine Warnung mit der folgenden Vorlage:

Die Non-Nullable-Eigenschaft Name muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten.

Es gibt zwei Möglichkeiten zur Lösung dieser Warnungen: Konstruktorverkettung oder Nullable-Attribute für die Hilfsmethode. 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 als Anmerkung 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";
    }
}

Hinweis

In C# 10 wurde die eindeutige Zuweisung und die NULL-Zustand-Analyse verbessert. Wenn Sie ein Upgrade auf C# 10 durchführen, werden möglicherweise weniger Nullable-Warnungen angezeigt, die falsch positiv sind. Weitere Informationen zu den Verbesserungen finden Sie in der Featurespezifikation für Verbesserungen an der eindeutigen Zuweisung.

Die Nullable-Zustandsanalyse und die vom Compiler generierten Warnungen helfen Ihnen, Programmfehler durch Dereferenzierung von null zu vermeiden. Im Artikel Auflösen von Warnungen in Bezug auf Nullwerte zulassende Ausdrücke werden Techniken zum Beheben der Warnungen vorgestellt, die wahrscheinlich in Ihrem Code angezeigt werden.

Attribute für API-Signaturen

Die NULL-Zustand-Analyse benötigt Hinweise von Entwickler*innen, um die Semantik von APIs zu verstehen. Einige APIs bieten NULL-Überprüfungen und sollten den null-state einer Variablen von maybe-null in not-null ändern. Andere APIs geben Ausdrücke zurück, die not-null oder maybe-null sind, je nach dem null-state der Eingabeargumente. Sehen Sie sich beispielsweise den folgenden Code an, der eine Meldung in Großbuchstaben anzeigt:

void PrintMessageUpper(string? message)
{
    if (!IsNull(message))
    {
        Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
    }
}

bool IsNull(string? s) => s == null;

Basierend auf der Überprüfung würde jeder Entwickler diesen Code als sicher betrachten, und er sollte keine Warnungen generieren. Der Compiler weiß jedoch nicht, dass IsNull eine NULL-Überprüfung bereitstellt und gibt eine Warnung für die message.ToUpper()-Anweisung aus, da er message für eine Variable vom Typ maybe-null hält. Verwenden Sie das NotNullWhen-Attribut, um diese Warnung zu beheben:

bool IsNull([NotNullWhen(false)] string? s) => s == null;

Dieses Attribut informiert den Compiler, dass, wenn IsNull false zurückgibt, der Parameter s nicht NULL ist. Der Compiler ändert den NULL-Zustand von message zu not-null im if (!IsNull(message)) {...}-Block. Es werden keine Warnungen ausgegeben.

Attribute bieten ausführliche Informationen zum NULL-Zustand von Argumenten, Rückgabewerten und Membern der Objektinstanz, die zum Aufrufen eines Members verwendet werden. Die Details zu den einzelnen Attributen finden Sie im Sprachreferenzartikel zu Nullable-Verweisattributen. Ab .NET 5 werden alle .NET-Laufzeit-APIs kommentiert. Sie verbessern die statische Analyse, indem Sie Ihre APIs kommentieren, um semantische Informationen zum null-state von Argumenten und Rückgabewerten zu liefern.

Nullable-Variablenanmerkungen

Die NULL-Zustand-Analyse bietet stabile Analysen für lokale Variablen. Der Compiler benötigt weitere Informationen von Ihnen für Membervariablen. Der Compiler benötigt weitere Informationen, um den NULL-Zustand aller Felder in der Eröffnungsklammer eines Members festzulegen. Jeder der zugreifbaren Konstruktoren kann verwendet werden, um das Objekt zu initialisieren. Wenn ein Memberfeld jemals auf null festgelegt werden kann, muss der Compiler davon ausgehen, dass sein null-status zu Beginn jeder Methode maybe-null ist.

Sie verwenden Anmerkungen, die deklarieren können, ob eine Variable ein Nullable-Verweistyp oder ein Nicht-Nullable-Verweistyp ist. Diese Anmerkungen enthalten wichtige Anweisungen zum null-state für Variablen:

  • Ein Verweis darf nicht NULL sein. Der Standardstatus einer keine Nullwerte zulassenden Verweisvariable ist not-null. Der Compiler erzwingt Regeln, die sicherstellen, dass das Dereferenzieren dieser Variablen sicher ist, ohne zuerst zu überprüfen, ob sie nicht NULL sind:
    • Die Variable muss mit einem Wert ungleich NULL initialisiert werden.
    • Der Variablen kann nie der Wert null zugewiesen werden. Der Compiler gibt eine Warnung aus, wenn der Code einen maybe-null-Ausdruck einer Variablen zuweist, die nicht NULL sein sollte.
  • Ein Verweis könnte NULL sein. Der Standardstatus einer Nullwerte zulassenden Verweisvariable ist maybe-null. Der Compiler erzwingt Regeln, um sicherzustellen, dass Sie ordnungsgemäß auf einen null-Verweis überprüfen:
    • Die Variable kann möglicherweise nur dereferenziert werden, wenn der Compiler garantieren kann, dass der Wert nicht null ist.
    • Diese Variablen könnten mit dem Standard-null-Wert initialisiert und in anderem Code dem Wert null zugewiesen werden.
    • Der Compiler gibt keine Warnungen aus, wenn Code einer Variablen, die NULL sein könnte, einen maybe-null-Ausdruck zuweist.

Jede keine Nullwerte zulassende-Verweisvariable hat einen Standard-NULL-Zustand von not-null. Jede Nullwerte zulassende Verweisvariable hat den ursprünglichen NULL-Zustand maybe-null.

Ein Nullable-Verweistyp wird mithilfe der gleichen Syntax wie Nullable-Werttypen aufgeführt: Ein ? wird an den Variablentyp angefügt. Beispielsweise stellt die folgende Variablendeklaration eine Nullable-Zeichenfolgenvariable, name, dar:

string? name;

Wenn Nullwerte zulassende Verweistypen aktiviert sind, ist jede Variable, bei der ? nicht an den Typnamen angefügt ist, ein keine Nullwerte zulassender Verweistyp. Dies umfasst alle Verweistypvariablen in vorhandenem Code, sobald Sie dieses Feature aktiviert haben. Alle implizit typisierten lokalen Variablen (die mit var deklariert wurden) sind Nullable-Verweistypen. Wie in den vorherigen Abschnitten gezeigt, bestimmt die statische Analyse den NULL-Zustand lokaler Variablen, um zu ermitteln, ob sie maybe-null sind bevor sie dereferenziert wird.

Manchmal müssen Sie eine Warnung überschreiben, wenn Sie wissen, dass eine Variable nicht NULL ist, der Compiler aber bestimmt, dass der null-statemaybe-null ist. Sie verwenden den NULL-toleranten Operator ! nach einem Variablennamen, um zu erzwingen, dass der null-state not-null ist. Wenn Sie beispielsweise wissen, dass die Variable name nicht null ist, der Compiler aber eine Warnung ausgibt, können Sie folgenden Code schreiben, um die Analyse des Compilers zu überschreiben:

name!.Length;

Nullwerte zulassende-Verweistypen und Nullwerte zulassende Werttypen bieten ein ähnliches semantisches Konzept: Eine Variable kann einen Wert oder ein Objekt darstellen, oder diese Variable könnte null sein. Nullable-Verweistypen und Nullable-Werttypen werden jedoch unterschiedlich implementiert: Nullable-Werttypen werden mit System.Nullable<T> implementiert, und Nullable-Verweistypen werden durch Attribute implementiert, die vom Compiler gelesen werden. string? und string werden z. B. beide durch den gleichen Typ dargestellt: System.String. int? und int werden jedoch durch System.Nullable<System.Int32> bzw. System.Int32 dargestellt.

Nullable-Verweistypen sind ein Kompilierzeitfeature. Dies bedeutet, dass Aufrufer Warnungen ignorieren und bewusst null als Argument für eine Methode verwenden können, die einen Non-Nullable-Verweis erwartet. Autoren von Bibliotheken sollten Laufzeitüberprüfungen auf NULL-Argumentwerte integrieren. ArgumentNullException.ThrowIfNull ist die bevorzugte Option zur Laufzeitüberprüfung eines Parameters auf NULL.

Wichtig

Das Aktivieren von Nullable-Anmerkungen kann die Art und Weise ändern, mit der Entity Framework Core bestimmt, ob ein Datenmember erforderlich ist. Weitere Einzelheiten finden Sie im Artikel Entity Framework Core-Grundlagen: Arbeiten mit Nullable-Verweistypen.

Generics

Generics erfordern detaillierte Regeln zur Behandlung von T? für jeden Typparameter T. Die Regeln sind aufgrund des bisherigen Verlaufs und der unterschiedlichen Implementierung für einen Nullwerte zulassende Werttyp und einen Nullwerte zulassende Verweistyp notwendigerweise ausführlich. Nullwerte zulassende Werttypen werden mit der Struktur System.Nullable<T> implementiert. Nullwerte zulassende Verweistypen werden als Typ-Anmerkungen implementiert, die dem Compiler semantische Regeln vorgeben.

  • Wenn das Typargument für T ein Verweistyp ist, verweist T? auf den entsprechenden Nullwerte zulassenden Verweistyp. Wenn zum Beispiel T ein string ist, dann ist T? ein string?.
  • Wenn das Typargument für T ein Wertetyp ist, verweist T? auf denselben Wertetyp, T. Wenn zum Beispiel T ein int ist, ist auch T? ein int.
  • Wenn das Typargument für T ein löschbarer Verweistyp ist, verweist T? auf denselben löschbaren Verweistyp. Wenn zum Beispiel T ein string? ist, dann ist T? auch ein string?.
  • Wenn das Typargument für T ein löschbarer Werttyp ist, verweist T? auf denselben löschbaren Werttyp. Wenn zum Beispiel T ein int? ist, dann ist T? auch ein int?.

Bei Rückgabewerten ist T? äquivalent zu [MaybeNull]T; für Argumentwerte ist T? äquivalent zu [AllowNull]T. Weitere Informationen finden Sie im Artikel Attribute für die Nullzustandsanalyse in der Sprachreferenz.

Mit Einschränkungen können Sie ein anderes Verhalten festlegen:

  • Die class-Einschränkung bedeutet, dass T ein keine Nullwerte zulassender Verweistyp sein muss (z. B. string). Der Compiler gibt eine Warnung aus, wenn Sie einen Nullwerte zulassenden Verweistyp verwenden, z. B. string? für T.
  • Die class?-Einschränkung bedeutet, dass T ein Verweistyp sein muss, entweder ein keine Nullwerte zulassender (string) oder ein Nullwerte zulassender Verweistyp (z. B. string?). Wenn der Typparameter ein löschbarer Verweistyp ist, z. B. string?, verweist ein Ausdruck von T? auf denselben löschbaren Verweistyp, z. B. string?.
  • Die notnull-Einschränkung bedeutet, dass T ein Non-Nullable-Verweistyp oder ein Non-Nullable-Werttyp sein muss. Wenn Sie einen Nullwerte zulassende Verweistyp oder einen Nullwerte zulassende Werttyp für den Typparameter verwenden, generiert der Compiler eine Warnung. Wenn T ein Werttyp ist, ist der Rückgabewert dieser Werttyp und nicht der entsprechende löschbare Werttyp.

Diese Einschränkungen helfen dem Compiler, weitere Informationen zur Verwendung von T zu erhalten. Dies hilft, wenn Entwickler*innen den Typ für T auswählen, und bietet eine bessere NULL-Zustand-Analyse, wenn eine Instanz des generischen Typs verwendet wird.

Nullable-Kontexte

Bei kleinen Projekten können Sie Nullwerte zulassende Verweistypen aktivieren, Warnungen beheben und fortfahren. Bei größeren Projekten und Projektlösungen mit mehreren Projekten kann dies jedoch zu einer großen Anzahl von Warnungen führen. Sie können Pragmen verwenden, um Nullwerte zulassende Verweistypen Datei-für-Datei zu aktivieren, während Sie sich mit der Verwendung von Nullwerte zulassenden Verweistypen beschäftigen. Die neuen Features, die vor dem Auslösen von System.NullReferenceException schützen, können störend sein, wenn sie in einer vorhandenen Codebasis aktiviert werden:

  • Alle explizit typisierten Verweisvariablen werden als Non-Nullable-Verweistypen interpretiert.
  • Die Bedeutung der class-Einschränkung in Generika wurde geändert, um einen Non-Nullable-Verweistyp anzugeben.
  • Aufgrund dieser neuen Regeln werden neue Warnungen generiert.

Der Nullable-Anmerkungskontext bestimmt das Verhalten des Compilers. Es gibt vier Werte für den Nullable-Anmerkungskontext:

  • disable: Der Code ist nullable-oblivious. Disable stimmt mit dem Verhalten überein, bevor Nullwerte zulassende Verweistypen aktiviert wurden. Die neue Syntax erzeugt allerdings Warnungen anstelle von Fehlern.
    • Nullable-Warnungen sind deaktiviert.
    • Alle Verweistypvariablen sind Nullable-Verweistypen.
    • Verwenden Sie das Suffix ? verwenden, um einen Nullable-Verweistyp zu deklarieren, der eine Warnung generiert.
    • Sie können den NULL-toleranten Operator (!) verwenden, das hat aber keine Auswirkungen.
  • enabled: Der Compiler aktiviert alle Features für die Nullverweisanalyse sowie alle Sprachfeatures.
    • Alle neuen Nullable-Warnungen sind aktiviert.
    • Sie können das Suffix ? verwenden, um einen Nullable-Verweistyp zu deklarieren.
    • Verweistypvariablen ohne das Suffix ? sind keine Nullwerte zulassenden Verweistypen.
    • Der Null-Verzeihungsoperator unterdrückt Warnungen für eine mögliche Ableitung von null.
  • warnings: Der Compiler führt alle NULL-Analysen durch und gibt Warnungen aus, wenn Code möglicherweise null dereferenziert.
    • Alle neuen Nullable-Warnungen sind aktiviert.
    • Verwenden Sie das Suffix ? verwenden, um einen Nullable-Verweistyp zu deklarieren, der eine Warnung generiert.
    • Alle Verweistypvariablen dürfen NULL sein. Member haben jedoch den null-statenot-null an der öffnenden geschweiften Klammer aller Methoden, es sei denn, sie werden mit dem Suffix ? deklariert.
    • Sie können den NULL-toleranten Operator (!) verwenden.
  • Anmerkungen: Der Compiler gibt keine Warnungen aus, wenn Code möglicherweise null dereferenzieren kann, oder wenn Sie einer keine Nullwerte zulassenden Variablen einen maybe-null-Ausdruck zuweisen.
    • Alle neuen Nullable-Warnungen sind deaktiviert.
    • Sie können das Suffix ? verwenden, um einen Nullable-Verweistyp zu deklarieren.
    • Verweistypvariablen ohne das Suffix ? sind keine Nullwerte zulassenden Verweistypen.
    • Sie können den NULL-toleranten Operator (!) verwenden, das hat aber keine Auswirkungen.

Der Nullable-Anmerkungskontext und der Nullable-Warnungskontext können für ein Projekt festgelegt werden, indem Sie das <Nullable>-Element in Ihrer .csproj-Datei verwenden. Dieses Element konfiguriert, wie der Compiler die NULL-Zulässigkeit von Typen interpretiert und welche Warnungen ausgegeben werden. In der folgenden Tabelle sind die zulässigen Werte aufgeführt und die von ihnen angegebenen Kontexte zusammengefasst.

Kontext Dereferenzierungswarnungen Zuweisungswarnungen Verweistypen ?-Suffix !-Operator
disable Disabled Disabled Alle lassen Nullwerte zu. Erzeugt eine Warnung Hat keine Auswirkungen.
enable Aktiviert Aktiviert Non-nullable, es sei denn, es wurde mit ? deklariert Deklariert einen Nullable-Typ Unterdrückt Warnungen für mögliche null-Zuweisungen.
warnings Aktiviert Nicht zutreffend Alle lassen Nullwerte zu, aber die Member werden bei der Eröffnungsklammer von Methoden als not-null betrachtet Erzeugt eine Warnung Unterdrückt Warnungen für mögliche null-Zuweisungen.
annotations Disabled Disabled Non-nullable, es sei denn, es wurde mit ? deklariert Deklariert einen Nullable-Typ Hat keine Auswirkungen.

Für Verweistypvariablen in Code, der in einem disable-Kontext kompiliert wurde, erfolgt keine Nullable-Beachtung. Sie können ein null-Literal oder eine maybe-null-Variable einer Variablen zuweisen, die nullable-oblivious ist. Der Standardzustand einer Variable mit der Eigenschaft Nullwerte zulassend-nicht beachtend ist jedoch nicht-null.

Sie können auswählen, welche Einstellung für Ihr Projekt am besten geeignet ist:

  • Wählen Sie disable für Legacyprojekte, die Sie nicht aufgrund von Diagnosen oder neuen Features aktualisieren möchten.
  • Wählen Sie Warnungen aus, um zu bestimmen, wo Ihr Code möglicherweise System.NullReferenceExceptions auslösen kann. Sie können diese Warnungen beheben, bevor Sie den Code ändern, um Non-Nullable-Verweistypen zu aktivieren.
  • Wählen Sie annotations aus, um Ihre Entwurfsabsicht auszudrücken, bevor Sie Warnungen aktivieren.
  • Wählen Sie enable für neue Projekte und aktive Projekte, die Sie vor NULL-Verweisausnahmen schützen möchten.

Beispiel:

<Nullable>enable</Nullable>

Sie können auch Anweisungen verwenden, um diese gleichen Kontexte an beliebiger Stelle in Ihrem Quellcode festzulegen. Diese Anweisungen sind besonders nützlich, wenn Sie eine große Codebasis migrieren.

  • #nullable enable: Legt den Nullable-Anmerkungskontext und den Nullable-Warnungskontext auf enable fest.
  • #nullable disable: Legt den Nullable-Anmerkungskontext und den Nullable-Warnungskontext auf disable fest.
  • #nullable restore: Stellt die Projekteinstellungen für den Nullable-Anmerkungskontext und den Nullable-Warnungskontext wieder her.
  • #nullable disable warnings: Legt den Nullable-Warnungskontext auf disable fest.
  • #nullable enable warnings: Legt den Nullable-Warnungskontext auf enable fest.
  • #nullable restore warnings: Stellt die Projekteinstellungen für den Nullable-Warnungskontext wieder her.
  • #nullable disable annotations: Legt den Nullable-Anmerkungskontext auf disable fest.
  • #nullable enable annotations: Legt den Nullable-Anmerkungskontext auf enable fest.
  • #nullable restore annotations: Stellt den Kontext mit nullablen Anmerkungen in den Projekteinstellungen wieder her.

Für jede Codezeile können Sie eine der folgenden Kombinationen festlegen:

Warnungskontext Anmerkungskontext Zweck
Standardeinstellung des Projekts Standardeinstellung des Projekts Standard
enable disable Analysewarnungen korrigieren
enable Standardeinstellung des Projekts Analysewarnungen korrigieren
Standardeinstellung des Projekts enable Typanmerkungen hinzufügen
enable enable Bereits migrierter Code
disable enable Kommentieren von Code vor dem Beheben von Warnungen
disable disable Hinzufügen von Legacycode zum migrierten Projekt
Standardeinstellung des Projekts disable Selten
disable Standardeinstellung des Projekts Selten

Mit diesen neun Kombinationen können Sie die Diagnosen, die der Compiler für Ihren Code ausgibt, detailliert steuern. Sie können weitere Features in jedem Bereich aktivieren, den Sie aktualisieren, ohne weitere Warnungen zu erhalten, die Sie noch nicht beheben möchten.

Wichtig

Der globale Nullable-Kontext gilt nicht für generierte Codedateien. Der Nullable-Kontext ist unabhängig von der Strategie für alle als generiert gekennzeichneten Quelldateien deaktiviert. Das bedeutet, dass alle in generierten Dateien enthaltenen APIs nicht mit Anmerkungen versehen werden. Es gibt viel Möglichkeiten, eine Datei als generiert zu markieren:

  1. Geben Sie in der EDITORCONFIG-Datei generated_code = true in einem Abschnitt an, der für diese Datei gilt.
  2. Fügen Sie <auto-generated> oder <auto-generated/> ganz oben in der Datei in einem Kommentar ein. Dabei kann es sich um eine beliebige Zeile des Kommentars handeln, jedoch muss es sich beim Kommentarblock um das erste Element in der Datei handeln.
  3. Beginnen Sie den Dateinamen mit TemporaryGeneratedFile_ .
  4. Enden Sie den Dateinamen mit .designer.cs, .generated.cs, .g.cs oder .g.i.cs.

Generatoren können die Präprozessoranweisung #nullable verwenden.

Standardmäßig sind die Nullable-Anmerkungs- und -Warnungskontexte deaktiviert. Dies bedeutet, dass Ihr vorhandener Code ohne Änderungen und ohne Warnungen kompiliert wird. Ab .NET 6 enthalten neue Projekte das <Nullable>enable</Nullable>-Element in allen Projektvorlagen.

Diese Optionen bieten zwei unterschiedliche Strategien zum Aktualisieren einer vorhandenen Codebasis, um Nullable-Verweistypen zu verwenden.

Bekannte Fehlerquellen

Arrays und Strukturen, die Verweistypen enthalten, sind bekannte Fallstricke in Nullable-Verweisen und in der statischen Analyse, die die NULL-Sicherheit bestimmt. In beiden Fällen kann ein keine Nullwerte zulassender-Verweis mit null initialisiert werden, ohne Warnungen zu generieren.

Strukturen

Strukturen, die Verweistypen enthalten, die keine NULL-Werte zulassen, können Sie default zuweisen, ohne dass Warnungen ausgelöst werden. Betrachten Sie das folgenden Beispiel:

using System;

#nullable enable

public struct Student
{
    public string FirstName;
    public string? MiddleName;
    public string LastName;
}

public static class Program
{
    public static void PrintStudent(Student student)
    {
        Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
        Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
        Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
    }

    public static void Main() => PrintStudent(default);
}

Im Beispiel oben gibt es in PrintStudent(default) keine Warnung, wenn die Non-Nullable-Verweistypen FirstName und LastName NULL sind.

Bei der Verwendung von generischen Strukturen tritt ein weiteres häufigeres Problem auf. Betrachten Sie das folgende Beispiel:

#nullable enable

public struct S<T>
{
    public T Prop { get; set; }
}

public static class Program
{
    public static void Main()
    {
        string s = default(S<string>).Prop;
    }
}

Im vorherigen Beispiel ist die Eigenschaft Prop bei Laufzeit null. Sie wird non-nullable Zeichenfolgen ohne Warnungen zugewiesen.

Arrays

Arrays stellen ebenfalls eine bekannte Fehlerquelle in Verweistypen dar, die NULL-Werte zulassen. Sehen Sie sich das folgende Beispiel an, das keine Warnungen auslöst:

using System;

#nullable enable

public static class Program
{
    public static void Main()
    {
        string[] values = new string[10];
        string s = values[0];
        Console.WriteLine(s.ToUpper());
    }
}

Im Beispiel oben zeigt die Deklaration des Arrays, dass dieses Non-Nullable-Zeichenfolgen enthält, während alle seine Elemente mit null initialisiert werden. Anschließend wird der Variablen s ein null-Wert (das erste Element des Arrays) zugewiesen. Schließlich wird die Variable s dereferenziert, was zu einer Laufzeitausnahme führt.

Siehe auch