int? 竟然真的可以是 null!.NET/C# 確定可空值類型 Nullable 實例的真實類型
- 2020 年 2 月 10 日
- 筆記
使用 Nullable<T>
我們可以為原本不可能為 null
的值類型像引用類型那樣提供一個 null
值。不過注意:Nullable<T>
本身也是個 struct
,是個值類型哦。這意味著你隨時可以調用 .HasValue
這樣的方法,而不用擔心會出現 NullReferenceException
。
等等!除了本文提到的一些情況。
注意看以下的程式碼。我們創建了一個值為 null
的 int?
,然後依次輸出 value
的值、value.GetType()
。
你覺得可以得到什麼結果呢?
public class Program { public static void Main(string[] args) { int? value = GetValue(null); Console.WriteLine($"value = {value}"); Console.WriteLine($"type = {value.GetType()}"); Console.WriteLine($"TYPE = {typeof(int?)}"); Console.ReadLine(); } private static int? GetValue(int? source) => source; }
結果是……
果是……
是……
……
…
崩掉了……

那麼我們在 value
後面加個空傳遞運算符:
-- Console.WriteLine($"type = {value.GetType()}"); ++ Console.WriteLine($"type = {value?.GetType()}");
現在再次運行,我們確認了 value?.GetType()
的值為 null
;而 typeof(int?)
的類型為 Nullable<Int32>
。

然而,我們現在將 value
的值從 null
改為 1
:
-- int? value = GetValue(null); ++ int? value = GetValue(1);
竟然 value.GetType()
得到的類型是 Int32
。

於是我們可以得出結論:
- 對於可空值類型,當為
null
時,GetType()
會出現空引用異常; - 對於可空值類型,當不為
null
時,GetType()
返回的是對應的基礎類型,而不是可空值類型; typeof(int?)
能夠得到可空值類型。
Object.GetType() 和 is 對 Nullable 的作用
在 docs.microsoft.com 中,有一段對此的描述:
When you call the Object.GetType method on an instance of a nullable type, the instance is boxed to Object. As boxing of a non-null instance of a nullable type is equivalent to boxing of a value of the underlying type, GetType returns a Type object that represents the underlying type of a nullable type.
意思是說,當你對一個可空值類型 Nullable<T>
調用 Object.GetType()
方法的時候,這個實例會被裝箱,會被隱式轉換為一個 object
對象。然而對可空值類型的裝箱與對值類型本身的裝箱是同樣的操作,所以調用 GetType()
的時候都是返回這個對象對應的實際基礎類型。例如對一個 int?
進行裝箱和對 int
裝箱得到的 object
對象是一樣的,於是 GetType()
實際上是不能區分這兩種情況的。
那什麼樣的裝箱會使得兩個不同的類型被裝箱為同一個了呢?
另一篇文檔描述了 Nullable<T>
裝箱的過程:
- If HasValue returns false, the null reference is produced.
- If HasValue returns true, a value of the underlying value type T is boxed, not the instance of Nullable.
- 如果
HasValue
返回false
,那麼就裝箱一個null
- 如果
HasValue
返回true
,那麼就將Nullable<T>
中的T
進行裝箱,而不是Nullable<T>
的實例。
這才是為什麼 GetType()
會得到以上結果的原因。
同樣的,也不能使用 is
運算符來確定這個類型到底是不是可空值類型:
Console.WriteLine($"value is int = {value is int}"); Console.WriteLine($"value is int? = {value is int?}");
最終得到兩者都是 True
。

應該如何判斷可空值類型的真實類型
使用 Nullable.GetUnderlyingType(type)
方法,能夠得到一個可空值類型中的基礎類型,也就是得到 Nullable<T>
中 T
的類型。如果得不到就返回 null
。
所以使用以下方法可以判斷 type
的真實類型。
bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;
然而,這個 type
的實例怎麼來呢?根據前面的示例程式碼,我們又不能調用 GetType()
方法。
實際上,這個 type
的實例就是拿不到,在運行時是不能確定的。我們只能在編譯時確定,就像下面這樣:
bool IsOfNullableType<T>(T _) => Nullable.GetUnderlyingType(typeof(T)) != null;
如果你是運行時拿到的可空值類型的實例,那麼實際上此方法也是無能為力的。
public class Program { public static void Main(string[] args) { Console.Title = "walterlv's demo"; int? value = GetValue(1); object o = value; Console.WriteLine($"value is nullable? {IsOfNullableType(value)}"); Console.WriteLine($"o is nullable? {IsOfNullableType(o)}"); Console.ReadLine(); } private static int? GetValue(int? source) => source; static bool IsOfNullableType<T>(T _) => Nullable.GetUnderlyingType(typeof(T)) != null; }

- c# – Nullable type is not a nullable type? – Stack Overflow
- How to: Identify a nullable type – C# Programming Guide – Microsoft Docs
- Using nullable types – C# Programming Guide – Microsoft Docs
本文會經常更新,請閱讀原文: https://blog.walterlv.com/post/how-to-identify-a-nullable-type.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名 呂毅 (包含鏈接: https://blog.walterlv.com ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。如有任何疑問,請 與我聯繫 ([email protected]) 。