Nullable-Verweistypen (C#-Referenz)

Hinweis

Dieser Artikel behandelt Nullable-Verweistypen. Sie können auch Nullable-Werttypen deklarieren.

Verwenden Sie nullable Verweistypen im Code, der sich in einem Kontext mit Nullablen befindet. Nullable-Verweistypen, Warnungen bei statischer Analyse des NULL-Status und der NULL-tolerante Operator sind optionale Sprachfeatures. Alle sind standardmäßig deaktiviert. Sie steuern einen nullfähigen Kontext auf Projektebene mithilfe von Buildeinstellungen oder im Code mithilfe von Pragmen.

Die C#-Sprachreferenz dokumentiert die zuletzt veröffentlichte Version der C#-Sprache. Außerdem enthält sie erste Dokumentation für Features in der öffentlichen Vorschau für die kommende Sprachversion.

In der Dokumentation werden alle Features identifiziert, die in den letzten drei Versionen der Sprache oder in der aktuellen öffentlichen Vorschau eingeführt wurden.

Tipp

Informationen dazu, wann ein Feature erstmals in C# eingeführt wurde, finden Sie im Artikel zum Versionsverlauf der C#-Sprache.

Wichtig

Alle Projektvorlagen ermöglichen die löschbarer Kontext für das Projekt. Projekte, die mit früheren Vorlagen erstellt wurden, enthalten dieses Element nicht, sodass diese Features deaktiviert sind, es sei denn, Sie aktivieren sie in der Projektdatei oder Sie verwenden Pragmas.

In einem NULL-kompatiblen Kontext gilt Folgendes:

  • Sie müssen eine Variable eines Bezugstyps T mit einem Wert ungleich NULL initialisieren, und Sie können niemals einen Wert zuweisen, der möglicherweise sein nullkann.
  • Sie können eine Variable eines Bezugstyps T? mit null oder zuweisen nullinitialisieren, aber Sie müssen sie null vor der Ableitung überprüfen.
  • Wenn Sie den Operator null-verzeihend auf eine Variable m des Typs T?anwenden , wie in m!, wird die Variable als nicht null angesehen.

Der Compiler erzwingt die Unterscheidung zwischen einem nicht nullfähigen Verweistyp und einem nullfähigen Verweistyp TT? mithilfe der vorherigen Regeln. Eine Variable vom Typ T und eine Variable vom Typ T? sind derselbe .NET-Typ. Im folgenden Beispiel werden eine Non-Nullable-Zeichenfolge und eine Nullable-Zeichenfolge deklariert. Anschließend wird der NULL-tolerante Operator verwendet, um einer Non-Nullable-Zeichenfolge einen Wert zuzuweisen:

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

Die Variablen notNull und nullable beide verwenden den String Typ. Da die nicht nullablen und nullablen Typen beide denselben Typ verwenden, können Sie keinen nullfähigen Verweistyp an mehreren Speicherorten verwenden. Im Allgemeinen können Sie keinen nullfähigen Verweistyp als Basisklasse oder implementierte Schnittstelle verwenden. Sie können keinen nullfähigen Verweistyp in einem Objekterstellungs- oder Typtestausdruck verwenden. Sie können keinen nullfähigen Verweistyp als Typ eines Elementzugriffsausdrucks verwenden. In den folgenden Beispielen werden diese Konstrukte veranschaulicht:

public MyClass : System.Object? // not allowed
{
}

var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
    if (thing is string? nullableString) // not allowed
        Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
    Console.WriteLine("error");
}

Nullable-Verweise und statische Analyse

Die Beispiele im vorherigen Abschnitt veranschaulichen den Charakter von Nullable-Verweistypen. Nullable-Verweistypen sind keine neuen Klassentypen, sondern eher Annotationen zu vorhandenen Verweistypen. Der Compiler verwendet diese Annotationen, um potenzielle NULL-Verweisfehler in Ihrem Code zu finden. Für Non-Nullable-Verweistypen und Nullable-Verweistypen werden keine unterschiedlichen Runtimes verwendet. Der Compiler fügt keine Runtime hinzu, die auf Non-Nullable-Verweistypen prüft. Die Vorteile liegen in der Analyse zur Kompilierzeit. Der Compiler generiert Warnungen, die Ihnen helfen, potenzielle NULL-Fehler in Ihrem Code zu finden und zu beheben. Sie deklarieren Ihre Absicht, und der Compiler warnt Sie, wenn Ihr Code gegen diese Absicht verstößt.

Wichtig

Nullable-Verweisanmerkungen führen keine Verhaltensänderungen ein, andere Bibliotheken verwenden jedoch möglicherweise Spiegelung, um ein anderes Laufzeitverhalten für nullable und nicht nullable Verweistypen zu erzeugen. Insbesondere verarbeitet Entity Framework Core nullable Attribute. Er interpretiert einen nullablen Bezug als optionalen Wert und einen nicht nullfähigen Verweis als erforderlichen Wert.

In NULL-kompatiblem Kontext führt der Compiler eine statische Analyse für Variablen für Nullable- und Non-Nullable-Verweistypen durch. Der Compiler erfasst den null-state jeder Verweisvariablen entweder als not-null (nicht NULL) oder als maybe-null (vielleicht NULL). Der Standardstatus eines nicht Nullwerte zulassenden Verweises ist not null (nicht NULL). Der Standardstatus eines Nullwerte zulassenden Verweises ist maybe null (vielleicht NULL).

Keine Nullwerte zulassende Verweistypen müssen immer sicher dereferenziert werden, da ihr NULL-Statusnot-null (nicht NULL) ist. Der Compiler erzwingt diese Regel, indem Warnungen ausgegeben werden, wenn ein Non-Nullable-Verweistyp nicht in einen Wert ungleich NULL initialisiert wird. Sie müssen lokale Variablen zuweisen, in denen Sie sie deklarieren. Jedem Feld muss in einem Feldinitialisierer bzw. in jedem Konstruktor ein Wert zugewiesen werden, der not-null (nicht Null) ist. Der Compiler gibt Warnungen aus, wenn ein keine Nullwerte zulassender Verweis einem Verweis zugeordnet wird, dessen Status maybe null (vielleicht NULL) ist. Im Allgemeinen ist ein nicht nullbarer Verweis nicht null , und beim Ableiten dieser Variablen werden keine Warnungen ausgegeben.

Hinweis

Wenn Sie einen maybe-null-Ausdruck einem Non-Nullable-Verweistyp zuweisen, erzeugt der Compiler eine Warnung. Der Compiler erzeugt dann Warnungen für diese Variable, bis sie einem Ausdruck zugewiesen wird, der not-null (nicht null) ist.

Sie können nullfähige Verweistypen initialisieren oder zuweisen null . Daher muss bei der statischen Analyse bestätigt werden, dass eine Variable not null (nicht NULL) ist, bevor sie dereferenziert wird. Wenn ein nullabler Verweis als möglicherweise NULL festgelegt wird, generiert das Zuweisen einer nicht nullablen Referenzvariablen eine Compilerwarnung. In der folgenden Klasse werden Beispiele für diese Warnungen veranschaulicht:

public class ProductDescription
{
    private string shortDescription;
    private string? detailedDescription;

    public ProductDescription() // Warning! shortDescription not initialized.
    {
    }

    public ProductDescription(string productDescription) =>
        this.shortDescription = productDescription;

    public void SetDescriptions(string productDescription, string? details=null)
    {
        shortDescription = productDescription;
        detailedDescription = details;
    }

    public string GetDescription()
    {
        if (detailedDescription.Length == 0) // Warning! dereference possible null
        {
            return shortDescription;
        }
        else
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
    }

    public string FullDescription()
    {
        if (detailedDescription == null)
        {
            return shortDescription;
        }
        else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
        return shortDescription;
    }
}

Im folgenden Codeausschnitt sehen Sie, an welcher Stelle der Compiler bei Verwendung dieser Klasse Warnungen ausgibt:

string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.

string description = "widget";
var item = new ProductDescription(description);

item.SetDescriptions(description, "These widgets will do everything.");

In den vorangehenden Beispielen wird die statische Analyse des Compilers veranschaulicht, mit der der NULL-Status von Verweisvariablen bestimmt wird. Der Compiler wendet Sprachregeln auf NULL-Prüfungen und -Zuweisungen an, um die Analyse mit Informationen zu versorgen. Der Compiler kann keine Annahmen zur Semantik von Methoden oder Eigenschaften treffen. Wenn Sie Methoden aufzurufen, die NULL-Prüfungen durchführen, kann der Compiler nicht wissen, welche Methoden den NULL-Status einer Variablen beeinflussen. Sie können Ihren APIs Attribute hinzufügen, um den Compiler über die Semantik von Argumenten und Rückgabewerten zu informieren. Viele gängige APIs in den .NET-Bibliotheken weisen diese Attribute auf. Der Compiler interpretiert z. B. IsNullOrEmpty korrekt als Nullprüfung. Weitere Informationen zu den Attributen für die statische Analyse des null-state finden Sie im Artikel zu Nullwerte zulassenden Attributen.

Nullable-Kontext

Der nullable Kontext bestimmt, wie der Compiler nullable Verweistypanmerkungen verarbeitet und welche Warnungen er während der statischen Nullzustandsanalyse erzeugt. Der Nullwert-Kontext enthält zwei Flags: die Einstellung für Anmerkungen und die Einstellung für Warnungen.

Sowohl die Anmerkungs--Einstellungen als auch die Warnungs--Einstellungen sind für vorhandene Projekte standardmäßig deaktiviert. Ab .NET 6 (C# 10) sind beide Flags standardmäßig für projekte new aktiviert. Der Grund für zwei unterschiedlichen Flags für den Nullwert-Kontext ist die Vereinfachung der Migration großer Projekte, die vor der Einführung von Nullwert-Referenztypen entwickelt wurden.

Bei kleinen Projekten können Sie Nullwerte zulassende Verweistypen aktivieren, die Warnungen beheben und dann fortfahren. Bei größeren Projekten und Projektlösungen mit mehreren Projekten kann dieser Prozess jedoch eine große Anzahl von Warnungen generieren. Sie können ein Pragma nutzen, um Nullwerte zulassende Verweistypen dateiweise zu aktivieren, während Sie beginnen, diese zu verwenden. 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 Kombinationen für die Einstellungen für den Nullwert-Kontext:

  • both disabled: 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 ?, um einen Nullable-Verweistyp zu deklarieren, der eine Warnung verursacht.
    • Sie können den NULL-toleranten Operator (!) verwenden, das hat aber keine Auswirkungen.
  • both enabled: Der Compiler unterstützt alle Nullreferenzanalysen und alle Sprachfunktionen.
    • Alle neuen Nullable-Warnungen sind aktiviert.
    • Sie können das Suffix ? verwenden, um einen Nullable-Verweistyp zu deklarieren.
    • Verweistypvariablen ohne das Suffix ? sind nicht nullbare Verweistypen.
    • Der Null-Verzeihungsoperator unterdrückt Warnungen für eine mögliche Ableitung von null.
  • warning enabled: Der Compiler führt alle Nullanalysen aus und gibt Warnungen aus, wenn Code null dereferenzieren könnte.
    • Alle neuen Nullable-Warnungen sind aktiviert.
    • Verwenden Sie das Suffix ?, um einen Nullable-Verweistyp zu deklarieren, der eine Warnung verursacht.
    • Alle Verweistypvariablen dürfen NULL sein. Mitglieder haben jedoch den null-statenot-null an der öffnenden geschweiften Klammer aller Methoden, es sei denn, sie werden mit dem Suffix ? angegeben.
    • Sie können den NULL-toleranten Operator (!) verwenden.
  • annotations enabled: Der Compiler gibt keine Warnungen aus, wenn Code null dereferenzieren könnte oder wenn Sie einem möglichen Nullwert-Ausdruck eine Nicht-Nullwert-Variable zuweisen.
    • Alle neuen Nullable-Warnungen sind deaktiviert.
    • Sie können das Suffix ? verwenden, um einen Nullable-Verweistyp zu deklarieren.
    • Verweistypvariablen ohne das Suffix ? sind nicht nullbare Verweistypen.
    • Sie können den NULL-toleranten Operator (!) verwenden, das hat aber keine Auswirkungen.

Mit dem <Nullable> Element in der CSPROJ-Datei können Sie den Kontext mit nullablen Anmerkungen und den Null-Warnkontext für ein Projekt festlegen. Dieses Element konfiguriert, wie der Compiler die Nullierbarkeit 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 Dereferenz-Warnungen Zuweisungswarnungen Referenztypen ?-Suffix !-Operator
disable Deaktiviert Deaktiviert Alle sind nullfähig. Erzeugt eine Warnung Hat keine Auswirkungen.
enable Aktiviert Aktiviert Nicht-nullfähig, außer wenn mit ? deklariert Deklariert einen Nullable-Typ Unterdrückt Warnungen für mögliche null-Zuweisungen.
warnings Aktiviert Nicht anwendbar 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 Deaktiviert Deaktiviert Nicht-nullfähig, außer wenn mit ? deklariert Deklariert einen Nullable-Typ Hat keine Auswirkungen.

Für Verweistypvariablen in Code, der in einem deaktivierten Kontext kompiliert wurde, sind nullable-unbeachtet. 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.

Wählen Sie die Einstellung aus, die am besten zu Ihrem Projekt passt:

  • 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 Direktiven verwenden, um dieselben Flags an einer beliebigen Stelle im Quellcode festzulegen. Diese Anweisungen sind besonders nützlich, wenn Sie eine große Codebasis migrieren.

  • #nullable enable: Legt die Anmerkungs- und Warnungsflags auf aktivieren fest.
  • #nullable disable: Legt die Anmerkungs- und Warnungsflags auf deaktivieren fest.
  • #nullable restore: Führt eine Wiederherstellung der Anmerkungs- und Warnungsflags zu den Projekteinstellungen aus.
  • #nullable disable warnings: Legt die zu deaktivierende Warnungskennzeichnung fest.
  • #nullable enable warnings: Legt das zu aktivierende Warnzeichen fest.
  • #nullable restore warnings: Führt eine Wiederherstellung des Warnungsflags auf die Projekteinstellungen aus.
  • #nullable disable annotations: Legt die zu deaktivierende Anmerkungskennzeichnung fest.
  • #nullable enable annotations: Legt die zu aktivierende Anmerkungskennzeichnung fest.
  • #nullable restore annotations: Führt eine Wiederherstellung des Anmerkungsflags auf die Projekteinstellungen aus.

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

Warnflagge Anmerkungsflag Verwendung
Standardeinstellung des Projekts Standardeinstellung des Projekts Vorgabe
ermöglichen disable Analysewarnungen korrigieren
ermöglichen Standardeinstellung des Projekts Analysewarnungen korrigieren
Standardeinstellung des Projekts ermöglichen Typanmerkungen hinzufügen
ermöglichen ermöglichen Bereits migrierter Code
disable ermöglichen 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

Diese neun Kombinationen bieten Ihnen eine fein abgestimmte Kontrolle über die Diagnose, die der Compiler für Ihren Code ausgibt. 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. Diese Bedingung bedeutet, dass der Compiler keine APIs in generierten Dateien kommentiert. Der Compiler erzeugt keine nullfähigen Warnungen für generierte Dateien. Eine Datei wird auf eine der folgenden vier Arten als generiert markiert:

  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 sich mit der #nullable Präprozessordirektive anmelden.

Standardmäßig sind Nullwert-Anmerkungs- und -Warnungsflags deaktiviert. Dies bedeutet, dass Ihr vorhandener Code ohne Änderungen kompiliert und ohne neue Warnungen generiert wird. Beginnend mit .NET 6 enthalten neue Projekte das <Nullable>enable</Nullable>-Element in allen Projektvorlagen und legen diese Flags auf enabled fest.

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

Festlegen des NULL-kompatiblen Kontexts

Sie können den nullfähigen Kontext auf zwei Arten steuern. Fügen Sie auf Projektebene die <Nullable>enable</Nullable> Projekteinstellung hinzu. Fügen Sie in einer einzelnen C#-Quelldatei das #nullable enable Pragma hinzu, um den nullfähigen Kontext zu aktivieren. Weitere Informationen finden Sie unter Festlegen einer nullfähigen Strategie. Vor .NET 6 verwenden neue Projekte die Standardeinstellung <Nullable>disable</Nullable>. Ab .NET 6 enthalten neue Projekte das <Nullable>enable</Nullable>-Element in allen Projektvorlagen.

Generika

Wenn Sie einen Typparameter verwenden, bestimmt das tatsächliche Typargument, Twie der ? Wert interpretiert wird, T?als nullwertes Gegenstück. Betrachten Sie die folgende generische Deklaration:

public class Box<T>
{
    public T Contents { get; set; }
}

Da ein Typparameter entweder für einen Bezugstyp oder einen Werttyp stehen kann, hängt die Bedeutung davon T? ab, welches Typargument der Aufrufer bereitstellt. In den folgenden Regeln wird beschrieben, was T? aufgelöst wird, wenn T keine Einschränkungen vorhanden sind:

  • Das Typargument ist ein nicht nullabler Bezugstyp. T string Ist Box<string>und T? ist string?- der entsprechende Nullwerte-Bezugstyp.
  • Das Typargument ist ein Werttyp. T int Ist Box<int>und T? ist auch intderselbe Werttyp. Die Anmerkung hat keine Auswirkung auf Werttypen, es sei denn, der Typparameter hat die struct Einschränkung, in diesem Fall T? bedeutet Nullable<T> (int?).
  • Das Typargument kann bereits null sein. Für Box<string?>, T ist string? und T? ist immer noch string?. Sie erhalten keinen Typ "doubly nullable".

Einschränkungen beschränken, welche Typargumente zulässig sind. Sie lassen auch den Compilergrund darüber zu, wie T verwendet werden kann:

  • where T : class erfordert einen nicht nullablen Verweistyp. Box<string> ist zulässig; Box<string?> erzeugt eine Warnung.
  • where T : class? ermöglicht entweder einen nullablen oder einen nicht nullablen Bezugstyp. Beide Box<string> und Box<string?> sind zulässig.
  • where T : struct erfordert einen Nicht-Null-Werttyp. Box<int> ist zulässig; Box<int?> Nicht. Mit dieser Einschränkung T? ist innerhalb der generischen Mittel Nullable<T>- für Box<int>, ist T?int?.
  • where T : notnull erfordert einen nicht nullfähigen Bezugs- oder Werttyp. Box<string> und Box<int> sind zulässig; Box<string?> erzeugt eine Warnung.
  • where T : BaseType erfordert einen nicht nullablen Verweistyp, der von BaseType. Append ? (where T : BaseType?) to allow nullable derived types as well.

Die Einschränkungen helfen dem Compiler, warum ein generischer Typparameter verwendet wird:

public static T? FirstOrDefault<T>(IEnumerable<T> source)
{
    foreach (T item in source)
    {
        return item;
    }
    return default;
}

public static void RequireNotNull<T>(T value) where T : notnull
{
    ArgumentNullException.ThrowIfNull(value);
}

public static void Generics()
{
    string? first = FirstOrDefault<string>([]);
    Console.WriteLine(first ?? "<empty>");

    RequireNotNull("not null");
}

C#-Sprachspezifikation

Weitere Informationen finden Sie im Abschnitt " Nullable reference types " der C#-Sprachspezifikation.

Weitere Informationen