可為 Null 的值類型 (C# 參考)

可為 Null 的實值型別T?代表其基礎實值型別T的所有值,以及額外的 Null 值。 例如,您可以將下列三個值中的任何一個指派給 bool? 變數:truefalsenull。 基礎實值型別 T 不能是可為 Null 的實值型別本身。

任何可為 Null 的實值型別都是泛型 System.Nullable<T> 結構的執行個體。 您可以使用下列任一可互換形式,參考具有基礎型別 T 的可為 Null 實值型別:Nullable<T>T?

當您需要表示基礎實值型別的未定義值時,通常會使用可為 Null 的實值型別。 例如,布林值或 bool、變數只能是 truefalse。 不過,在某些應用程式中,變數值可能未定義或遺失。 例如,資料庫欄位可能包含 truefalse,或是完全不包含任何值,也就是 NULL。 您可以在該情節中使用 bool? 型別。

宣告與指派

與實值型別可隱含轉換為對應可為 Null 的實值型別相同,您可以用為其基礎實值型別指派值的相同方式,將值指派給可為 Null 實值型別的變數。 您也可以指派 null 值。 例如:

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];

可為 Null 之實值型別的預設值代表 null,也就是其 Nullable<T>.HasValue 屬性傳回 false 的執行個體。

檢查可為 Null 實值型別的執行個體

您可以搭配型別模式使用 is 運算子來檢查 null 可為 Null 實值型別的執行個體,以及擷取基礎型別的值:

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

您一律可以使用下列唯讀屬性,來檢查和取得可為 Null 實值型別變數的值:

下列範例會使用 HasValue 屬性,在顯示之前測試變數是否包含值:

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

您也可以如以下範例所示,使用 null 而非使用 HasValue 屬性,比較可為 Null 實值型別的變數:

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

從可為 Null 實值型別轉換至基礎型別

如果您想要將可為 Null 實值型別的值指派給不可為 Null 的實值型別變數,您可能需要指定要指派的值來取代 null。 使用 null 聯合運算子 ?? 來執行該作業 (您也可以將 Nullable<T>.GetValueOrDefault(T) 方法用於相同的用途):

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

如果您想要使用基礎實值型別的預設值取代 null,請使用 Nullable<T>.GetValueOrDefault() 方法。

如下列範例所示,您也可以明確地將可為 Null 的實值型別轉換成不可為 Null 的型別:

int? n = null;

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

在執行階段,若可為 Null 實值型別的值為 null,則明確轉換會擲回 InvalidOperationException

不可為 Null 的實值型別 T 會隱含轉換成對應的可為 Null 實值型別 T?

增益運算子

對應的可為 Null 實值型別 T? 也支援預先定義的一元和二進位運算子或實值型別 T 支援的任何多載運算子。 若其中一或兩個運算元皆為 null,名為增益運算子的這些運算子便會產生 null;否則,運算子會使用運算元包含的值來計算結果。 例如:

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

注意

針對 bool? 型別,預先定義的 &| 運算子不會遵循本節中描述的規則:即使其中一個運算元為 null,運算子評估的結果也可能為非 Null。 如需詳細資訊,請參閱布林值邏輯運算子一文的可為 Null 的布林值邏輯運算子一節。

如果是比較運算子<><=>=,如果一或兩個運算元都是 null,則結果為 false,否則會比較運算元的內含值。 請不要因為特定的比較 (例如 <=) 傳回 false,就假設相反的比較 (>) 就會傳回 true。 下列範例會顯示 10

  • 既不大於也不等於 null
  • 也不小於 null
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

如果是相等運算子==,如果兩個運算元都是 null,則結果為 true,如果只有其中一個運算元為 null,則結果為 false;否則會比較運算元的內含值。

如果是不相等運算子!=,如果兩個運算元都是 null,則結果為 false,如果只有其中一個運算元為 null,則結果為 true;否則會比較運算元的內含值。

如果兩個實值型別之間有使用者定義的轉換,則對應可為 Null 的實值型別之間也可以使用相同的轉換。

Box 處理和 Unbox 處理

可為 Null 實值型別 T? 的執行個體會以下列方式進行 Box 處理

  • HasValue 傳回 false,則會產生 Null 參考。
  • HasValue 傳回 true,則基礎實值型別 T 的對應值為 boxed,而非 Nullable<T> 的執行個體。

如下列範例所示,您可以將實值型別為 T 的 boxed 值 unbox 為對應的可為 Null 實值型別 T?

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

如何識別可為 Null 的實值型別

下列範例示範如何判斷 System.Type 執行個體是否代表建構可為 Null 的實值型別,也就是,System.Nullable<T> 型別具有指定的型別參數 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

如範例所示,您使用 typeof 運算子來建立 System.Type 執行個體。

如果您想要判斷某個執行個體是否屬於可為 Null 的實值型別,請勿使用 Object.GetType 方法透過上述程式碼來測試 Type 執行個體。 當您在可為 Null 的實值型別執行個體上呼叫 Object.GetType 方法時,該執行個體會 boxed 處理為 Object。 由於對可為 Null 實值型別的非 Null 執行個體進行 Boxing 處理相當於對基礎型別的值進行 Boxing 處理,因此 GetType 會傳回 Type 執行個體,代表可為 Null 實值型別的基礎型別:

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

此外,請勿使用 is 運算子,來判斷某個執行個體是否屬於可為 Null 的實值型別。 如下列範例所示,您無法使用 is 運算子,來區別可為 Null 實值型別及其基礎型別的執行個體型別:

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?

請改用第一個範例中的 Nullable.GetUnderlyingTypetypeof 運算子,來檢查執行個體是否為可為 Null 的實值型別。

注意

本節所述的方法不適用於可為 Null 的參考型別中的案例。

C# 語言規格

如需詳細資訊,請參閱 C# 語言規格的下列幾節:

另請參閱