實值型別 (int、double、bool、DateTime、struct) 預設不可為 null。C# 2.0 引入 Nullable<T> 結構與 ? 語法糖,讓實值型別能表達「無值」狀態。本文詳細說明宣告、核心屬性、運算規則、巢狀限制與實務模式。
// 完整泛型語法
Nullable nullableInt = null;
Nullable nullableDouble = 3.14;
// 語法糖 (慣用)
int? myInt = null;
int? myInt2 = 42;
double? myDouble = 3.14;
bool? flag = null;
DateTime? birthDate = null;
// 結構體也適用
struct Point { public int X, Y; }
Point? nullablePoint = null;
public struct Nullable where T : struct
{
private readonly bool _hasValue;
private readonly T _value;
public bool HasValue => _hasValue;
public T Value => _hasValue ? _value : throw new InvalidOperationException();
public T GetValueOrDefault() => _value;
public T GetValueOrDefault(T defaultValue) => _hasValue ? _value : defaultValue;
// 隱含/顯式轉換運算子...
}
| 成員 | 說明 | 範例 |
|---|---|---|
| HasValue | 是否有值 (true/false) | int? x = null; x.HasValue == false |
| Value | 取得值 (無值時拋出例外) | int? x = 5; x.Value == 5 |
| GetValueOrDefault() | 取得值或預設值 (不拋出例外) | int? x = null; x.GetValueOrDefault() == 0 |
| GetValueOrDefault(T) | 取得值或指定預設值 | int? x = null; x.GetValueOrDefault(-1) == -1 |
int? a = 10;
int? b = null;
int? c = 5;
// 算術運算:任一運算元為 null → 結果為 null
int? sum = a + b; // null
int? diff = a - c; // 5
int? prod = a * c; // 50
int? quot = a / c; // 2
// 比較運算:任一為 null → 結果為 false (除 != 外)
bool eq = (a == b); // false (非 true)
bool ne = (a != b); // true
bool lt = (a < c); // false
bool gt = (a > c); // true
// 邏輯運算 (bool?)
bool? x = true;
bool? y = null;
bool? z = false;
bool? and = x & y; // null (三值邏輯)
bool? or = y | z; // null
bool? not = !y; // null
// ❌ 編譯錯誤 CS0453: 型別引數必須是非可為 Null 的實值型別
Nullable> nested = null;
int?? doubleNullable = null; // 語法糖也禁止
原因:Nullable<T> where T : struct 限制,而 Nullable<T> 本身是 struct,但被視為「可為 Null 的型別」,不滿足條件。
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int? Age { get; set; } // 對應 DB INT NULL
public DateTime? LastLogin { get; set; } // 對應 DB DATETIME NULL
public decimal? Salary { get; set; }
}
public void Search(string keyword, int? pageSize = null, int? pageIndex = null)
{
int size = pageSize.GetValueOrDefault(20);
int index = pageIndex.GetValueOrDefault(0);
// ...
}
bool? userConsent = null; // null=未詢問, true=同意, false=拒絕
if (userConsent == true) { /* 同意 */ }
else if (userConsent == false) { /* 拒絕 */ }
else { /* 顯示同意對話框 */ }
// 模式 1: HasValue 檢查
if (nullableInt.HasValue)
Console.WriteLine(nullableInt.Value);
// 模式 2: GetValueOrDefault
Console.WriteLine(nullableInt.GetValueOrDefault(0));
// 模式 3: ?? 空合併運算子 (最推薦)
Console.WriteLine(nullableInt ?? 0);
// 模式 4: 模式比對 (C# 7+)
if (nullableInt is int value)
Console.WriteLine(value);
else
Console.WriteLine("無值");
int? a = 42;
int? b = null;
object boxedA = a; // 裝箱為 int (非 Nullable!)
object boxedB = b; // 裝箱為 null 參考
Console.WriteLine(boxedA.GetType()); // System.Int32
Console.WriteLine(boxedB); // null
// 解封箱
int unboxedA = (int)boxedA; // 正常
// int unboxedB = (int)boxedB; // NullReferenceException
int? unboxedB = (int?)boxedB; // 正常,結果為 null
Nullable<T> (語法糖 T?) 讓實值型別獲得「無值」表達能力。核心在於 HasValue/Value 雙屬性設計、提升運算規則、?? 空合併運算子配合。禁止巢狀 Nullable<Nullable<T>> 簡化型別系統。資料庫映射、選擇性參數、三狀態邏輯是主要應用場景。