Nullable-Werttypen (C#-Referenz)

Ein Werttyp, der NULL zulässt (wie T?) stellt alle Werte des zugrunde liegenden Werttyps T und einen zusätzlichen NULL-Wert dar. Beispielsweise können Sie einer bool?-Variablen einen der folgenden drei Werte zuweisen: true, false oder null. Ein zugrunde liegender Werttyp T darf selbst kein Nullable-Werttyp sein.

Hinweis

C# 8.0 führt das Feature der Nullable-Verweistypen ein. Weitere Informationen finden Sie unter Nullable-Verweistypen. Nullable-Werttypen sind ab C# 2 verfügbar.

Jeder Nullable-Werttyp ist eine Instanz der generischen Struktur System.Nullable<T>. Sie können auf einen Nullable-Werttyp mit einem zugrunde liegenden Typ T in einer der folgenden austauschbaren Formen verweisen: Nullable<T> oder T?.

Sie verwenden in der Regel einen Nullable-Werttyp, wenn Sie den nicht definierten Wert eines zugrunde liegenden Werttyps darstellen müssen. Beispielsweise kann ein boolescher Wert eine bool-Variable nur true oder false sein. In einigen Anwendungen kann ein Variablenwert jedoch nicht definiert sein oder fehlen. Beispielsweise kann ein Datenbankfeld true oder falseenthalten, oder es enthält gar keinen Wert, ist also NULL. In diesem Szenario können Sie den bool?-Typ verwenden.

Deklaration und Zuweisung

Da ein Werttyp implizit in den entsprechenden Nullable-Werttyp konvertiert werden kann, können Sie einer Variablen eines Nullable-Werttyps auf die gleiche Weise wie seinem zugrunde liegenden Werttyp einen Wert zuweisen. Sie können auch den null-Wert zuweisen. Zum Beispiel:

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable value type:
int?[] arr = new int?[10];

Der Standardwert eines Nullable-Werttyps stellt null dar, es handelt sich also um eine-Instanz, deren Nullable<T>.HasValue-Eigenschaft false zurückgibt.

Untersuchung einer Instanz eines Nullable-Werttyps

Ab C# 7.0 können Sie den is-Operator mit einem Typmuster verwenden, um eine Instanz eines Nullable-Werttyps für null zu untersuchen und einen Wert eines zugrunde liegenden Typs abzurufen:

int? a = 42;
if (a is int valueOfA)
{
    Console.WriteLine($"a is {valueOfA}");
}
else
{
    Console.WriteLine("a does not have a value");
}
// Output:
// a is 42

Sie können immer die folgenden schreibgeschützten Eigenschaften verwenden, um einen Wert einer Variablen des Nullable-Werttyps zu untersuchen und abzurufen:

Im folgenden Beispiel wird mit der HasValue-Eigenschaft überprüft, ob die Variable einen Wert enthält. Erst danach erfolgt die Anzeige:

int? b = 10;
if (b.HasValue)
{
    Console.WriteLine($"b is {b.Value}");
}
else
{
    Console.WriteLine("b does not have a value");
}
// Output:
// b is 10

Sie können eine Variable eines Nullable-Werttyps wie im folgenden Beispiel gezeigt auch mit null anstelle der HasValue-Eigenschaft vergleichen:

int? c = 7;
if (c != null)
{
    Console.WriteLine($"c is {c.Value}");
}
else
{
    Console.WriteLine("c does not have a value");
}
// Output:
// c is 7

Konvertieren eines Nullable-Werttyps in einen zugrunde liegenden Typ

Wenn Sie einen Wert eines Nullable-Werttyps einer Variablen eines Nicht-Nullable-Werttyps zuweisen möchten, müssen Sie möglicherweise den zuzuweisenden Wert anstelle von null angeben. Verwenden Sie zu diesem Zweck den NULL-Sammeloperator ?? (Sie können auch die Nullable<T>.GetValueOrDefault(T)-Methode für denselben Zweck verwenden):

int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}");  // output: b is 28

int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}");  // output: d is -1

Wenn Sie den Standardwert des zugrunde liegenden Werttyps anstelle von null verwenden möchten, verwenden Sie die Nullable<T>.GetValueOrDefault()-Methode.

Sie können einen Nullable-Werttyp wie im folgenden Beispiel gezeigt auch explizit in einen Non-Nullable-Typ umwandeln:

int? n = null;

//int m1 = n;    // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null

Wenn der Wert eines Nullable-Typs zur Laufzeit null ist, wird durch die explizite Umwandlung eine InvalidOperationException ausgelöst.

Ein Nicht-Nullable-Werttyp T kann implizit in den entsprechenden Nullable-Werttyp T? konvertiert werden.

„Lifted“ Operatoren

Die vordefinierten unären und binären Operatoren oder alle überladenen Operatoren, die von einem Werttyp T unterstützt werden, werden auch vom entsprechenden Werttyp T? unterstützt, der NULL zulässt. Durch diese Operatoren (auch als „lifted“ Operatoren bezeichnet) wird null generiert, wenn mindestens ein Operand null ist. Andernfalls verwendet der Operator die enthaltenen Werte seiner Operanden zur Berechnung des Ergebnisses. Zum Beispiel:

int? a = 10;
int? b = null;
int? c = 10;

a++;        // a is 11
a = a * c;  // a is 110
a = a + b;  // a is null

Hinweis

Für den bool?-Typ gelten für die vordefinierten Operatoren & und | nicht die in diesem Abschnitt beschriebenen Regeln. Die Auswertung eines Operators kann auch dann einen anderen Wert als NULL ergeben, wenn einer der beiden Operanden null ist. Weitere Informationen finden Sie im Abschnitt Boolesche logische Operatoren, die NULL-Werte zulassen im Artikel Boolesche logische Operatoren.

Für die Vergleichsoperatoren <, >, <= und >= ist das Ergebnis false, wenn einer oder beide Operanden null sind. Andernfalls werden die enthaltenen Werte von Operanden verglichen. Falsch wäre die Annahme, dass im Fall des Rückgabewerts false für einen bestimmten Vergleich (beispielsweise <=) der gegenteilige Vergleich (>) true zurückgibt. Das folgende Beispiel zeigt, dass 10

  • weder größer als oder gleich null
  • noch kleiner als null ist.
int? a = 10;
Console.WriteLine($"{a} >= null is {a >= null}");
Console.WriteLine($"{a} < null is {a < null}");
Console.WriteLine($"{a} == null is {a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False

int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {b >= c}");
Console.WriteLine($"null == null is {b == c}");
// Output:
// null >= null is False
// null == null is True

Für den Gleichheitsoperator == ist das Ergebnis true, wenn beide Operanden null sind. Das Ergebnis ist false, wenn nur einer der Operanden null ist. Andernfalls werden die enthaltenen Werte von Operanden verglichen.

Für den Ungleichheitsoperator != ist das Ergebnis false, wenn beide Operanden null sind. Das Ergebnis ist true, wenn nur einer der Operanden null ist. Andernfalls werden die enthaltenen Werte von Operanden verglichen.

Wenn eine benutzerdefinierte Konvertierung zwischen zwei Werttypen vorhanden ist, kann die gleiche Konvertierung auch zwischen den entsprechenden Nullable-Werttypen verwendet werden.

Boxing und Unboxing

Das Boxing einer Instanz eines Nullable-Werttyps T? erfolgt folgendermaßen:

  • Wenn HasValuefalse zurückgibt, wird der Nullverweis erstellt.
  • Wenn HasValuetrue zurückgibt, wird nicht für eine Instanz von Nullable<T>, sondern für den entsprechenden Wert des zugrunde liegenden Werttyps T Boxing durchgeführt.

Sie können den Werttyp, für den das Boxing durchgeführt wurde, vom Werttyp T mittels Unboxing in den entsprechenden Nullable-Werttyp T? konvertieren, wie im folgenden Beispiel gezeigt wird:

int a = 41;
object aBoxed = a;
int? aNullable = (int?)aBoxed;
Console.WriteLine($"Value of aNullable: {aNullable}");

object aNullableBoxed = aNullable;
if (aNullableBoxed is int valueOfA)
{
    Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41

Identifizieren eines Nullable-Werttyps

Das folgende Beispiel zeigt, wie Sie bestimmen können, ob eine System.Type-Instanz einen konstruierten Nullable-Werttyp darstellt, d.h. den System.Nullable<T>-Typ mit einem angegebenen Typparameter T:

Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

Wie Sie im Beispiel sehen können, wird der Operator typeof verwendet, um eine System.Type-Instanz zu erstellen.

Wenn Sie bestimmen möchten, ob eine Instanz einen Nullable-Werttyp aufweist, verwenden Sie nicht die Object.GetType-Methode, um eine Type-Instanz abzurufen, die mit dem vorangehenden Code überprüft werden soll. Wenn Sie die Object.GetType-Methode in einer Instanz eines Nullable-Werttyps aufrufen, wird die Instanz in Objectgeschachtelt. Da das Schachteln einer nicht-NULL-Instanz von einem Nullable-Werttyp dem Schachteln eines Werts des zugrunde liegenden Typs entspricht, gibt GetType eine Type-Instanz zurück, die den zugrunde liegenden Typ eines Nullable-Werttyps darstellt:

int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32

Verwenden Sie außerdem nicht den Operator is, um zu ermitteln, ob eine Instanz einem Nullable-Werttyp entspricht. Wie im folgenden Beispiel veranschaulicht wird, können Sie die Typen einer Instanz eines Nullable-Werttyps und die zugrunde liegende Typinstanz nicht mithilfe des is-Operators unterscheiden:

int? a = 14;
if (a is int)
{
    Console.WriteLine("int? instance is compatible with int");
}

int b = 17;
if (b is int?)
{
    Console.WriteLine("int instance is compatible with int?");
}
// Output:
// int? instance is compatible with int
// int instance is compatible with int?

Sie können den Code verwenden, der im folgenden Beispiel dargestellt wird, um zu ermitteln, ob eine Instanz einen Nullable-Werttyp aufweist:

int? a = 14;
Console.WriteLine(IsOfNullableType(a));  // output: True

int b = 17;
Console.WriteLine(IsOfNullableType(b));  // output: False

bool IsOfNullableType<T>(T o)
{
    var type = typeof(T);
    return Nullable.GetUnderlyingType(type) != null;
}

Hinweis

Die in diesem Abschnitt beschriebenen Methoden sind im Fall von Nullable-Verweistypen nicht anwendbar.

C#-Sprachspezifikation

Weitere Informationen finden Sie in den folgenden Abschnitten der C#-Sprachspezifikation:

Siehe auch