首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

C# 可為 Null 的型別完整指南:Nullable<T>、空合併運算子、可為 Null 參考型別與最佳實踐

     編輯:C# 入門

C# 可為 Null 的型別完整指南:Nullable<T>、空合併運算子、可為 Null 參考型別與最佳實踐

「空參考」被稱為「十億美元的錯誤」。C# 提供兩套機制處理「缺少值」:可為 Null 的實值型別(Nullable<T>/T?)與 C# 8.0+ 可為 Null 的參考型別(string?、List<T>?)。正確使用這兩套機制,能在編譯期消除大量 NullReferenceException。

可為 Null 的實值型別

實值型別(int、bool、DateTime、struct)本身不可為 null。Nullable<T> 為其包裝一層,增加「無值」狀態:

// 語法糖:T? 等同 Nullable
int? count = null;
int? count2 = 42;
bool? flag = null;
DateTime? birthday = null;

// 底層結構
// public struct Nullable where T : struct {
//     public bool HasValue { get; }
//     public T Value { get; } // HasValue=false 時擲出例外
//     public T GetValueOrDefault() => HasValue ? Value : default;
//     public T GetValueOrDefault(T defaultValue) => HasValue ? Value : defaultValue;
// }

核心屬性與方法

int? x = 5;
int? y = null;

x.HasValue;           // true
y.HasValue;           // false
x.Value;              // 5
// y.Value;           // InvalidOperationException
x.GetValueOrDefault(); // 5
y.GetValueOrDefault(); // 0 (int 預設值)
y.GetValueOrDefault(-1); // -1

// 隱式轉換:T -> T?
int a = 10;
int? b = a; // 隱式

// 顯式轉換:T? -> T (可能擲出)
int c = (int)x; // 或 x.Value
int d = y ?? -1; // 空合併運算子 (推薦)

空合併運算子

??:提供預設值

int? nullable = null;
int value = nullable ?? 42; // 42

// 鏈式
string? s1 = null, s2 = null, s3 = "hello";
string result = s1 ?? s2 ?? s3 ?? "default"; // "hello"

// 條件存取配合
var length = user?.Profile?.Name?.Length ?? 0;

??=`:唯賦值當為 null

List? list = null;
list ??= new List(); // 只有 null 時初始化
list.Add("item");

可為 Null 的參考型別

C# 8.0+ 編譯器追蹤參考型別的 null 狀態,需在專案啟用:


  enable     
  nullable 

或檔案層級:#nullable enable / #nullable disable / #nullable restore。

型別標註

string? nullableString = null;    // 可為 null
string nonNullString = "hello";   // 非 null (編譯器強制初始化)
List? maybeList = null;      // 可為 null
List list = new();           // 非 null

靜態分析規則

程式碼編譯器判斷警告/錯誤
string s = null;非 null 型別指派 nullCS8600/CS8625
string? s = null; s.Length可能為 null 解參考CS8602
string s = GetString(); s.Length若 GetString 回傳 string? 則警告CS8602
string s = GetString()!;Null-forgiving 運算子,開發者承擔風險無 (壓制警告)
string s = GetString() ?? "default";空合併後確定非 null無

常見模式

// 1. 防禦性檢查
public void Process(string? input) {
    if (input is null) throw new ArgumentNullException(nameof(input));
    // 此處 input 判斷為非 null
    Console.WriteLine(input.Length);
}

// 2. 模式比對
if (input is { Length: > 0 } str) { // 非 null 且長度 > 0
    Console.WriteLine(str);
}

// 3. 建構子初始化非 null 屬性
public class User {
    public string Name { get; } // 非 null
    public User(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
}

// 4. 必要屬性
public class Config {
    public required string ConnectionString { get; init; } // C# 11+
}

進階主題

泛型約束

// T 為實值型別,可用 T?
public struct NullableWrapper where T : struct { }

// T 為參考型別,T? 表示可為 null
public class Repository where T : class { 
    public T? FindById(int id) => null; // 回傳可為 null
}

// notnull 約束:T 必須為非 null 型別 (實值型別或非 null 參考型別)
public class Cache where TKey : notnull { }

互通性屬性

為舊版程式庫/外部 API 提供 null 狀態提示:

// 回傳值可能為 null
[return: MaybeNull] public string GetOrDefault(int id) => null;

// 參數不應為 null
public void SetName([NotNull] string name) { }

// 參數可能為 null
public void Log([AllowNull] string message) { }

// 條件式 null 狀態
public bool TryGetValue(string key, [MaybeNullWhen(false)] out string value) { ... }

最佳實踐

  • 新專案啟用 <Nullable>enable</Nullable>:從第一行程式碼受益編譯期保護。
  • 避免 ! 運算子:只有確定安全(如 DI 容器注入、單元測試模擬)才使用。
  • 公開 API 明確標註:參數/回傳值用 ? 表達意圖,減少呼叫端猜測。
  • 實值型別用 T?、參考型別用 T? (啟用功能時),語意統一。
  • 空合併運算子優於三元/if:x ?? default 簡潔且編譯器理解結果非 null。
  • 避免「原始」型別:List 而非 List<object>,泛型保留 null 狀態資訊。

常見問題 FAQ

Q: Nullable<T> 是類別還是結構?

A: 是結構體(struct),所以本身不可為 null,佔用記憶體為 T + bool。

Q: double.NaN vs double? 代表「無值」?

A: double? 語意更清晰、型別安全、可用 HasValue 檢查。NaN 仍是有效 double 值,參與運算會傳播,易造成邏輯錯誤。

Q: 既有專案如何漸進啟用?

A: 1) 專案檔加 <Nullable>enable</Nullable> 2) 逐檔 #nullable disable 暫時關閉 3) 修復警告 4) 移除 disable 5) 加 WarningsAsErrors。

Q: 陣列/集合的 null 狀態?

A: string?[]:陣列本身可為 null,元素可為 null。string[]?:陣列可為 null,元素非 null。List<string?>:集合非 null,元素可為 null。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved