CodeGym /課程 /C# SELF /處理 null:運算子 ??、?.、?、!、default

處理 null:運算子 ??、?.、?、!、default

C# SELF
等級 14 , 課堂 3
開放

1. Null 合併運算子 (??)

你對 nullable 型別的理解如果沒有學會怎麼寫簡潔又安全的語法就不算完整。在 C# 裡有兩個超好用的運算子:null 合併 (??)null 條件運算子 (?.)

?? 運算子 讓你可以給一個可能是 null 的變數設定「預設值」。

想像一下,你有個使用者名稱,可能是 null。如果真的是 null,你想顯示「訪客」。範例:

string userName = null;
string displayName = userName != null ? userName : "訪客";
經典的 null 判斷寫法

?? 可以寫得更精簡:

string userName = null;
string displayName = userName ?? "訪客";
?? 設定預設值

怎麼運作:

  • 如果左邊的表達式不是 null,就直接回傳左邊。
  • 如果左邊是 null,就用右邊的值。

再來幾個例子:

int? age = null;
int displayAge = age ?? -1; // -1 — 預設值
Console.WriteLine(displayAge); // 會印出 -1

string input = null;
string name = input ?? "訪客";
Console.WriteLine($"哈囉, {name}!"); // 哈囉, 訪客!

運算子串接

?? 可以串成一條鏈:

string result = str1 ?? str2 ?? "預設值";
?? 運算子串接

如果 str1 不是 null,就用它。否則就用 str2,如果連 str2 也是 null,那就用 "預設值"。

2. Null 條件運算子 (?.)

另一個常見情境——只有在物件不是 null 的時候才呼叫方法或存取屬性。範例:

User user = null;
string displayName = user != null ? user.Name : null;
存取屬性前的經典 null 判斷

?. 可以更簡單:

User user = null;
string displayName = user?.Name;
Null 條件運算子 ?.

如果 usernull,這個表達式不會噴錯,只會回傳 null

範例:

User user = null;

// 沒有 ?. — 會噴錯
// Console.WriteLine(user.Name); // NullReferenceException

Console.WriteLine(user?.Name); // 安全 — 會印出空字串或什麼都沒有

Console.WriteLine(user?.GetProfileInfo()); // 一樣安全

User[] users = null;
int? count = users?.Length; // 如果 users == null,count 也是 null

運算子鏈

你可以串成一整條「鏈」:


string domain = company?.Director?.Email?.Split('@')?[1];
?. 串接安全存取巢狀屬性

如果路徑上任何一個是 null,整個表達式就會回傳 null,不會噴例外。

?? 一起用

?.?? 超搭:


string display = user?.Name ?? "未知";
安全輸出+預設值

如果 useruser.Namenull,就會顯示「未知」。

3. ! 是什麼?有什麼用?

抑制 null 警告的運算子

在新版 C#(開啟 NRT 時)編譯器會貼心提醒你,如果你用到可能是 null 的變數。但有時你很確定沒問題,這時就可以用 抑制運算子 !


string? possibleNull = GetUserNameMaybeNull();
Console.WriteLine(possibleNull.Length); // 警告:可能是 null

Console.WriteLine(possibleNull!.Length); // 編譯器不吭聲,但如果真的是 null 會噴例外

重點: ! 不會幫你擋錯誤——它只是跟編譯器說「相信我啦」。如果變數真的等於 null,還是會噴 NullReferenceException

什麼時候不該用

千萬不要亂用 !。好習慣是盡量少用它。最好讓你的程式碼設計到 null 不可能發生,或是有明確處理。

4. default — 怎麼拿到型別的「預設值」

有時候你只是想把變數「重設」回初始狀態,尤其不想自己手動寫 0falsenull

這時就有 default 關鍵字。

int a = default;            // a == 0
bool flag = default;        // flag == false
string s = default;         // s == null
double? d = default;        // d == null
default 重設變數

你的小 app 裡可以這樣重設名字或年齡:

userName = default; // null
userAge = default;  // null(如果 userAge 是 int?)

5. 差異與細節:?!default

就算是老手,有時看到 ?!default 也會搞混。來釐清一下誰是誰。

? — 你允許 null

  • 值型別:int? x — x 可以是 null
  • 參考型別:string? s — 明確說這變數可以是 null(NRT 模式下)。

! — 你保證不會有 null

  • user!.Name — 你跟編譯器保證 user 絕對不是 null
  • 只在 Nullable Reference Types 模式下有用。

default — 你要「預設值」

  • int x = default; — x 會變成 0
  • string s = default; — s 會變成 null

比較:

語法 這是什麼? 哪裡能用? null 有啥關係?
int?
Nullable 值型別 到處都能用 可以賦值 null
string?
Nullable 參考型別(NRT) NRT 模式下 可以賦值 null
!
Null-forgiving operator NRT 模式下 抑制警告
default
預設值 到處都能用 參考型別就是 null

6. 用 ?!default 常見錯誤

錯誤 1:以為 ! 能「治癒」null。
其實 ! 只是讓編譯器閉嘴。如果值真的變成 null,程式還是會噴 NullReferenceException

錯誤 2:用 .Value 前忘了檢查 .HasValue
特別是 int?bool? 跟其他 nullable 型別。沒檢查就用會噴 InvalidOperationException

錯誤 3:在需要「特殊」零值語意時用 default
比如 0false 可能是有效值,不是「空」的意思。這樣會有邏輯 bug。

錯誤 4:在 NRT 模式下沒用 ? 處理參考型別——或亂用。
有人忘了加 ?,編譯器就會一堆警告。有人則到處亂加 ?,連根本不會有 null 的地方也加。這兩種都會讓程式碼難讀又不穩。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION