C# null 安全性

小提示

本文屬於 基礎部分, 適合已經至少懂一種程式語言並正在學習 C# 的開發者。 如果你是程式新手,建議先從 入門 教學開始。

你是從 Java 還是 C++ 轉過來? C# 透過可空性參考型別提供編譯時的 null 安全。 這個目標類似於 Java 的 @NonNull 註解,但由編譯器強制執行。 C# 也有專用的運算子,比如 ?.?? ,讓空安全表達式變得簡潔。

null 代表不存在一個值。 當你嘗試透過呼叫方法或讀取屬性來存取參考中的成員 null 時,執行時會拋出一個 NullReferenceException

// Accessing a member on null throws NullReferenceException at runtime:
// string? name = null;
// int length = name.Length; // throws NullReferenceException

// Check before you dereference:
string? name = null;
if (name is not null)
{
    Console.WriteLine($"Name has {name.Length} characters.");
}
else
{
    Console.WriteLine("Name has no value.");
}
// Output: Name has no value.

C# 提供三種互補工具來撰寫空安全程式碼:

  • 可空值類型:讓像 intbool 這樣的值類型也能持有null
  • 可控參考類型:使編譯器能夠追蹤參考是否可能為 null
  • 空運算子:簡潔地表達空安全存取與備援邏輯

可為 Null 的實值型別

預設情況下,像 intdoublebool 這樣的值類型無法持有 null。 在型別名稱中加入 ? 以建立 一個可空的值型別 ,該值可包含 或 null

int? score = null;
Console.WriteLine(score.HasValue);               // False

score = 95;
Console.WriteLine(score.HasValue);               // True
Console.WriteLine(score.GetValueOrDefault());    // 95

int? missing = null;
Console.WriteLine(missing.GetValueOrDefault(-1)); // -1

當底層值類型需要代表「無資料」時,可空值型別非常有用。常見的情況包括可能缺失的資料庫欄位、可選的設定,以及尚未被捕捉到的感測器讀數。

關於宣告、檢查與轉換的完整說明,請參見 Nullable value types

可為空的參考類型

參考型別,如 string、陣列和類別實例,可以在執行時保持 null可空參考型別 是編譯器的功能,能明確表示空意圖並在編譯時捕捉錯誤。

透過使用 ? 註解,你宣告了你的意圖:

  • string?—這個參考可能是null;如果你未先檢查就解引用,編譯器會發出警告。
  • string — 此參考 不應該null;編譯器會在你指派 null 時警告
// string?  means this reference might be null
// string   means this reference should not be null
string? nullableName = null;
string  nonNullName  = "Alice";

// ?. safely accesses a member when the reference might be null
string display = nullableName?.ToUpper() ?? "(no name)";
Console.WriteLine(display);         // (no name)

display = nonNullName.ToUpper();    // safe: nonNullName is never null
Console.WriteLine(display);         // ALICE

所有最新 SDK 範本所建立的 .NET 專案預設會啟用可為 null 的參考型別。 欲了解啟用與註解的完整指引,請參見 可空參考類型

零運算元

C# 包含數個運算子,使你能撰寫安全的 null 程式碼,而不需要在各處手動設定 ifnull 檢查。

操作員 Name Purpose
?. 空條件成員存取 只有當物件非空時才存取成員
?[] 空條件索引器存取 只有當集合非空時才存取元素
?? 零聚合 返回預設值當表達式為null
??= 空值合併賦值運算 僅在變數為 null 時進行指定
is null / is not null Null 模式 偏好的零檢定
string? city = GetCity();

// ?. — access a member only when non-null
int? len = city?.Length;

// ?? — substitute a default when null
string display = city ?? "unknown";

// is null — preferred null test
if (city is null)
{
    Console.WriteLine("No city provided.");
}
else
{
    Console.WriteLine($"{display} ({len} chars)");
}
// Output: No city provided.

關於每個運算子的詳細範例,請參見 空運算子

可空值型別與可空參考型別有不同的用途

可空值型別和可空參考型別不是替代方案。 它們解決了不同的問題:

  • T? 表示「無值」的值類型。例如,int? 可用於可選的資料庫欄位,而 DateTime? 則適用於尚未排程的事件。
  • 使用 string? 及其他可為 null 的參考註解來說明某個參考可能為,這樣編譯器就能在執行階段發生null前提醒你。

這些功能與 null 運算子結合,提供了完整的工具組合來撰寫 null 安全的 C# 程式碼。