C# Nullable 不可為空 使用示範

在 C# 8.0 的時候推出了「可為 Null 的參考型別」,在官方文件說明中表示 Nullable 是參考型別的註釋,不是新的類別型別,在執行階段並沒有差異,而是在編譯時期分析中提出警告,方便找出程式碼中的淺在錯誤。

宣告:
    
int a = 0; // 不可為空
int b = null; // 如果被賦值為 null 時會出現 Cannot convert source type 'null' to target type 'int'
int? c = null; // 可為空
    

在平時使用時有什麼差別呢?最大的問題在於數字運算的時候,不論是 int, float, double 等數字型別,如果宣告為可空時,會無法直接計算:
    
int? a = 0;
int? b = null;

int c = a + b; // Error CS0266 : 無法將類型 'int?' 隱含轉換成 'int'。已存在明確轉換 (是否漏了轉型?)
    

一般最常見的解決方式就是使用顯式轉換 (explicit),將可空的 int 轉換為不可空的 int ,直接計算:
    
int? a = 0;
int? b = null;

// int c = a + b; // Error CS0266 : 無法將類型 'int?' 隱含轉換成 'int'。已存在明確轉換 (是否漏了轉型?)
int c = (int)a + (int)b; 
    

但是這樣如果遇到內容是 null ,就會拋出例外:
    
Unhandled exception. System.InvalidOperationException: Nullable object must have a value.
    

也可以透過以下幾種方式先檢查可為空的變數是否是空,然後給予預設值或是執行轉換:
    
int c = (a.HasValue ? a.Value : 0) + (b.HasValue ? b.Value : 0);
    

簡化後:
    
int c = (a ?? 0) + (b ?? 0);
    

或是: (在 .NET 6 下測試,不保證所有版本都相同)
    
int c = a ?? 0 + b ?? 0; // 測試環境 .NET 6
    

太麻煩了吧,為了處理到底是不是 Null 要這麼費工,那我不就設為 int ,不要使用 int? 就好了?
在舊的版本中 Nullable 這個功能是可選的還未啟用,但是後來的版本是預先啟用。這麼麻煩就是避免程式實際執行時明明應該是數字卻遇到 Null 這樣的「例外情況」。不過其實除了上面很麻煩的檢查外有一個方法非常好用:

在可空的類型中可以使用 GetValueOrDefault ,例如在 int 中預設值是 0 ,所以是 Null 時就會自動賦予 0 :
    
int c = a.GetValueOrDefault() + b.GetValueOrDefault();
    

不過萬一是除法呢?在大多數程式中除以 0 則是會拋出例外:
    
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
    

對耶,這樣是不是還要再判斷是不是 0?
GetValueOrDefault 可以帶入參數,如果是 null 的話除了賦予預設值外還可以指定預設值:
    
int c = a.GetValueOrDefault() / b.GetValueOrDefault(1);
    

這樣看起來 Nullable 是否就變得更好用了呢?不過筆者覺得從根本上來說還是要盡量避免內容會是 Null,如果本來預期內容不會是 Null,結果出現了 Null,那應該要拋出例外,畢竟不符合邏輯,Nullable 只是提示我們需要注意而已。

參考資料:
Microsoft.Learn - The history of C# - C# version 8.0
Microsoft.Learn - Nullable reference types (C# reference)
Microsoft.Learn - Nullable<T>.GetValueOrDefault Method

留言