1. 前言
想像一下這種情境:你在程式裡存貓咪的資料。除了像年齡這種資訊,你還想存「沒資料」、「年齡未知」這種狀態。你或許會想用 0 來代表,但 0 其實是個有效的年齡啊。
int age = 0; // 貓的年齡。但 0 是什麼?小貓還是「未知」?
問題來了,int 只能存整數,沒辦法表達「沒資料」這種狀態。如果年齡是字串,還可以設 null。但數字就不行啦:
int age = null; // 錯誤! 不能把 null 指派給 int 變數
這是因為 值型(像 struct,例如 int、double、DateTime、bool)一定要有值。它們沒有「空」的狀態(不像參考型,null 就是沒有物件)。
生活中的例子:
有些工程師會用「特殊」數值來繞過這個限制,比如 -1 或 int.MaxValue 代表「沒值」。但這樣很醜、很麻煩,也很危險:很容易把真的值跟假值搞混。
2. Nullable 類型:可以有 "null" 的型別
概念
如果讓一般的數字(或所有值型)也能存 null,會怎樣?
C# 用一個特別的包裝類 Nullable<T> 來實現。這個類可以存一個 T 型別的值,或什麼都沒有(也就是 null)。
語法
int? age = null; // 變數 age 可以是數字,也可以是 null
C# 編譯器看到這種寫法,其實會當成:
Nullable<int> age = new Nullable<int>(); // 變數 age 沒有值
或者你想直接給值:
Nullable<int> age = new Nullable<int>(42); // age 裡面是 42
所以問號會把任何值型變成它的 nullable 版本:
- int?(等同 Nullable<int>)
- double?
- DateTime?
- 還有其他 struct 都可以
常見 struct 的 Nullable 包裝
| 一般型別 | Nullable 型別 | 寫法範例 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
你也可以寫長一點,用包裝類的寫法:
Nullable<int> pages = null;
但寫 int? 比較短,也比較帥。
3. 怎麼用 nullable 類型
指派跟檢查
指派 null 跟參考型一樣簡單:
宣告跟指派
int? temperature = null;
temperature = 25;
檢查是不是 null
怎麼知道 nullable 型別裡有沒有值?
if (temperature != null)
{
Console.WriteLine($"溫度:{temperature}");
}
else
{
Console.WriteLine("沒有溫度資料");
}
Nullable 型別的屬性:.HasValue 跟 .Value
nullable 型別有兩個很重要的屬性:
- .HasValue — 如果變數有值(不是 null),就回傳 true。
- .Value — 直接拿值(如果有,不然會丟例外)。
int? temperature = null;
if (temperature.HasValue) //不會丟例外!
{
Console.WriteLine($"溫度:{temperature.Value}°C");
}
else
{
Console.WriteLine("溫度未知");
}
這段程式會動:temperature 不是 null,它裡面包著 null。你隨時可以叫 Nullable 型別的 HasValue。但如果沒值還硬要拿 .Value,就會噴 InvalidOperationException。記得先檢查有沒有值!
簡化寫法
很多時候你直接用 nullable 變數,C# 會自己處理:
int? hours = null;
Console.WriteLine(hours); // 不會印東西(就是空的)
hours = 10;
Console.WriteLine(hours); // 印出 10
4. 運算跟 nullable 類型:算術、比較、型別轉換
運算:會發生什麼?
只要有一個運算元是 nullable,結果也是 nullable。
如果有一個運算元是 null,結果也會是 null。
int? a = 5;
int? b = null;
int? sum = a + b; // sum == null
int? c = 10;
int? d = 15;
int? total = c + d; // total == 25
比較運算
你可以拿 nullable 類型跟一般數字比大小:
int? score = null;
if (score > 0) Console.WriteLine("很棒的結果!");
else Console.WriteLine("沒有結果"); // 會跑這裡
如果 score 是 null,score > 0 會是 false。
明確跟隱含型別轉換
- 從一般型別轉 nullable:自動轉。
- 從 nullable 轉一般型別:只有不是 null 才行(不然會丟例外)。
int? x = 3;
int y = x.Value; // OK,只要 x 不是 null
// 隱含轉換
int? z = y;
5. Nullable 跟方法:參數跟回傳值
回傳 nullable 值
如果方法不一定能回傳結果,可以用 nullable 型別當回傳值:
// 方法根據登入找使用者,找到就回傳年齡,沒找到就回傳 null
int? FindUserAge(string login)
{
// ... 這裡是搜尋邏輯
return null; // 沒找到
}
Nullable 參數
你也可以讓參數是 nullable,這樣就很明確:「可能有,也可能沒有」。
void PrintTemperature(int? temp)
{
if (temp == null)
Console.WriteLine("溫度未知");
else
Console.WriteLine($"溫度:{temp} 度");
}
GO TO FULL VERSION