.NET中可空值類型實現原理


為了讓.Net中的值類型可以賦值為null,微軟特地添加了Nullable<T>類型,也可簡寫為T?。但是Nullable<T>自身是結構體,也是值類型,那么它是如何實現將null賦值給值類型的呢?

下面通過自定義一個可空值類型來講解Nullable<T>的實現原理。

自定義可空值類型

struct XfhNullable<T> where T : struct
{
    private T innerValue;
    //這個屬性很重要
    public bool HasValue { set; get; }
    public T Value
    {
        get
        {
            return HasValue ? innerValue: throw new InvalidOperationException();
        }
    }

    public XfhNullable(T value)
    {
        this.innerValue= value;
        HasValue = true;
    }

    public T GetValueOrDefault(T value)
    {
        return HasValue ? this.innerValue: value;
    }
    public T GetValueOrDefault()
    {
        return this.innerValue;
    }
}

一個可空值類型的結構體大致功能已經定義好了,下面我們來創建可空值類型的實例來驗證下。

using static System.Console;

class Program
{
    static void Main()
    {
        //使用結構體默認的無參構造函數進行實例化
        XfhNullable<int> num = new XfhNullable<int>();
        WriteLine(num.HasValue);
        WriteLine(null_num.GetValueOrDefault());
    }
}

可以看到,變量num並不含有值,調用GetValueOrDefault()則會獲取它的默認值 0;

 

這時我們將null賦值給變量num會發現編譯器報錯Cannot convert null to 'XfhNullable<int>' because it is a non-nullable value type這是因為編譯器把我們定義的結構體XfhNullable<T>看作是普通值類型而非可空值類型,所以我們還要添加可空值類型和XfhNullable<T>之間的轉換功能。

public static implicit operator XfhNullable<T>(T? nullabelValue)
{
    if (nullabelValue== null)
    {
        return new XfhNullable<T>();
    }
    return new XfhNullable<T>(nullabelValue.Value);
}

上面的代碼實現了可空值類型向XfhNullable<T>的隱式轉換,添加上面代碼之后發現編譯器不再報錯。XfhNullable<T>已經成為一個可為null的值類型。

static void Main()
{
    XfhNullable<int> null_num = null;
    WriteLine(null_num.HasValue);
}

XfhNullable<T>中的屬性HasValue的作用就是標記當前類型是否為null,若是則返回False,否則返回True。當HasValueFalse時調用該類型的Value屬性則會拋出異常InvalidOperationException。但可調用GetValueOrDefault()方法來獲取類型的默認值。

 

Nullable<T>類型可以通過運算符==來判斷值是否為null,我們也可以通過運算符重載來實現該功能:

public static bool operator ==(XfhNullable<T> cn, object obj)
{
    if (cn.HasValue)
    {
        return false;
    }
    return true;
}
public static bool operator !=(XfhNullable<T> cn, object obj)
{
    return !(cn == obj);
}
static void Main()
{
    XfhNullable<int> null_num = null;
    WriteLine(null_num == null);
}
 

接下來,我們來實現普通值類型和XfhNullable<T>之間的轉換:

public static implicit operator XfhNullable<T>(T value)
{
    return new XfhNullable<T>(value);
}
public static explicit operator T(XfhNullable<T> value)
{
    return value.innerValue;
}
static void Main()
{
    XfhNullable<int> null_num = null;
    null_num = 12;//int類型隱式轉換為XfhNullable<int>類型
    WriteLine(null_num == null);
    WriteLine(null_num.Value);
    int i = (int)null_num;//XfhNullable<int>類型強制轉換為int類型
    WriteLine(i);
}
 

獲取實例在運行時的類型:

static void Main()
{
    XfhNullable<int> null_num = 12;
    WriteLine(null_num.GetType());
}

這個返回值不大友好,我們希望這里返回內置的值類型,System.Int32,具體實現代碼如下:

//因為Object類中的GetType方法不允許子類重寫(避免子類隱藏自己的實際類型)
//所以這里使用關鍵字new來隱藏Object類中的GetType方法
public new Type GetType()
{
    return innerValue.GetType();
}
 

結論:沒有可為空的值類型

至此,我們已經自定義了一個可為空的值類型XfhNullable<T>,通過以上代碼,我們不難發現所謂可為空的值類型是不存在的,它是通過屬性HasValue來對null值進行標記的,其內部通過字段innerValue(該字段對應Nullable<T>中的value字段)來維護該類型的值,若被賦值為null則innerValue初始化為值類型的初始值。換句話說,Nullable<T>只是在邏輯層面上實現了把null賦值給值類型,給我們一種值類型可為null的感覺

最后說下可空值類型的裝箱與拆箱。
CLR在對Nullable<T>實例執行裝箱操作時首先檢查它是否為null,若是則CLR不裝箱任何東西而是直接返回null;若實例的值不是null則獲取該實例的值(Value屬性)並對這個值進行裝箱操作。
拆箱時,對於null則返回一個Nullable<T>()實例,對於一個具體的數值,如5,則返回Nullable<T>(5)實例。

版權聲明

本文為作者原創,版權歸作者雪飛鴻所有。 轉載必須保留文章的完整性,且在頁面明顯位置處標明原文鏈接

如有問題, 請發送郵件和作者聯系。


免責聲明!

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



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