int? nullableInt = 10; // 語法糖
Nullable nullableInt2 = 10; // 完整寫法,等價
// Nullable where T : struct
Nullable<T> 是**結構體**,包含兩個公開唯讀屬性:
- bool HasValue:是否有值
- T Value:取得值 (若 HasValue==false 拋 InvalidOperationException)
int? a = 100; // 有值
int? b = null; // 無值
int? c = default; // 無值 (default(Nullable) = null)
int? d = new int?(); // 無值
| 方式 | 程式碼 | 行為 |
|---|---|---|
| HasValue 檢查 | if (a.HasValue) Console.WriteLine(a.Value); | 安全,無例外 |
| GetValueOrDefault() | int val = a.GetValueOrDefault(); (預設 0)int val = a.GetValueOrDefault(-1); (自訂預設) | 無值時回傳預設值,不拋例外 |
| Null 條件運算子 (C# 6) | int? len = str?.Length; | 鏈式存取安全 |
| Null 合併運算子 ?? (C# 2) | int val = a ?? -1; | 無值時使用右側值 |
| 模式比對 (C# 7) | if (a is int v) Console.WriteLine(v); | 同時檢查並取值 |
int? x = 10, y = 5;
int? sum = x + y; // 15
int? diff = x - y; // 5
int? prod = x * y; // 50
int? quot = x / y; // 2
int? mod = x % y; // 0
bool? gt = x > y; // true (bool?)
bool? eq = x == y; // false (bool?)
// 任一運算元為 null → 結果為 null
int? a = 10;
object boxed = a; // 裝箱:若 a.HasValue → boxed 為 int (非 Nullable)
// 若 a == null → boxed 為 null
// 拆箱需小心
object obj = 10;
int? unboxed = (int?)obj; // 正確:obj 為 int → 隱性轉換為 int?
// object obj2 = null;
// int? unboxed2 = (int?)obj2; // 正確:null → int? null
// 要求 T 為不可為 null 的值型別
public void Process(T value) where T : struct
{
// T 可以是 int, int?, DateTime, DateTime? 等
// 但不可為 class 或 interface
}
// 要求 T 為可為 null 的型別 (值型別或參考型別)
public void ProcessNullable(T? value) where T : struct
{
// T? 等同 Nullable
}
// ADO.NET 讀取可為 NULL 的欄位
int? age = reader["Age"] as int?;
// 或
int? age = reader.IsDBNull(ordinal) ? null : reader.GetInt32(ordinal);
// Entity Framework Core 自動對應
public class User { public int? Age { get; set; } } // 對應 nullable int column
DateTime? BirthDate 優於 DateTime BirthDate + 魔術值 (DateTime.MinValue)。int?? 編譯錯誤。.Value 直接存取拋例外風險。int? 序列化為 null 或數字,符合預期。Nullable 型別讓值型別也能表達「缺失」語意,是現代 C# 類型系統的重要基石。