int? 竟然真的可以是 null!.NET/C# 確定可空值類型 Nullable 實例的真實類型


使用 Nullable<T> 我們可以為原本不可能為 null 的值類型像引用類型那樣提供一個 null 值。不過注意:Nullable<T> 本身也是個 struct,是個值類型哦。這意味着你隨時可以調用 .HasValue 這樣的方法,而不用擔心會出現 NullReferenceException

等等!除了本文提到的一些情況。


 

 

Nullable 中的 null

注意看以下的代碼。我們創建了一個值為 nullint?,然后依次輸出 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;
}

 

結果是……

 

果是……

 

是……

 

……

 

 

崩掉了……

NullReferenceException

那么我們在 value 后面加個空傳遞運算符:

--  Console.WriteLine($"type  = {value.GetType()}");
++  Console.WriteLine($"type  = {value?.GetType()}");

現在再次運行,我們確認了 value?.GetType() 的值為 null;而 typeof(int?) 的類型為 Nullable<Int32>

null 的類型

然而,我們現在將 value 的值從 null 改為 1

--  int? value = GetValue(null);
++  int? value = GetValue(1);

竟然 value.GetType() 得到的類型是 Int32

1 的類型

於是我們可以得出結論:

  1. 對於可空值類型,當為 null 時,GetType() 會出現空引用異常;
  2. 對於可空值類型,當不為 null 時,GetType() 返回的是對應的基礎類型,而不是可空值類型;
  3. 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

用 is 確定類型

應該如何判斷可空值類型的真實類型

使用 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;
}

運行時是拿不到的


參考資料


我的博客會首發於 https://walterlv.com/,而 CSDN 和博客園僅從其中摘選發布,而且一旦發布了就不再更新。

知識共享許可協議

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名呂毅(包含鏈接:https://blog.csdn.net/wpwalter),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM