.NET中可空值類型實(shí)現(xiàn)原理

為了讓.Net中的值類型可以賦值為null,微軟特地添加了Nullable<T>類型梁呈,也可簡寫為T?婚度。但是Nullable<T>自身是結(jié)構(gòu)體,也是值類型官卡,那么它是如何實(shí)現(xiàn)將null賦值給值類型的呢蝗茁?

下面通過自定義一個(gè)可空值類型來講解Nullable<T>的實(shí)現(xiàn)原理。

自定義可空值類型

struct XfhNullable<T> where T : struct
{
    private T innerValue;
    //這個(gè)屬性很重要
    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;
    }
}

一個(gè)可空值類型的結(jié)構(gòu)體大致功能已經(jīng)定義好了寻咒,下面我們來創(chuàng)建可空值類型的實(shí)例來驗(yàn)證下哮翘。

using static System.Console;

class Program
{
    static void Main()
    {
        //使用結(jié)構(gòu)體默認(rèn)的無參構(gòu)造函數(shù)進(jìn)行實(shí)例化
        XfhNullable<int> num = new XfhNullable<int>();
        WriteLine(num.HasValue);
        WriteLine(null_num.GetValueOrDefault());
    }
}

可以看到,變量num并不含有值毛秘,調(diào)用GetValueOrDefault()則會獲取它的默認(rèn)值 0饭寺;

這時(shí)我們將null賦值給變量num會發(fā)現(xiàn)編譯器報(bào)錯(cuò)Cannot convert null to 'XfhNullable<int>' because it is a non-nullable value type這是因?yàn)榫幾g器把我們定義的結(jié)構(gòu)體XfhNullable<T>看作是普通值類型而非可空值類型,所以我們還要添加可空值類型和XfhNullable<T>之間的轉(zhuǎn)換功能叫挟。

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

上面的代碼實(shí)現(xiàn)了可空值類型向XfhNullable<T>的隱式轉(zhuǎn)換艰匙,添加上面代碼之后發(fā)現(xiàn)編譯器不再報(bào)錯(cuò)。XfhNullable<T>已經(jīng)成為一個(gè)可為null的值類型抹恳。

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

XfhNullable<T>中的屬性HasValue的作用就是標(biāo)記當(dāng)前類型是否為null员凝,若是則返回False,否則返回True奋献。當(dāng)HasValueFalse時(shí)調(diào)用該類型的Value屬性則會拋出異常InvalidOperationException健霹。但可調(diào)用GetValueOrDefault()方法來獲取類型的默認(rèn)值旺上。

Nullable<T>類型可以通過運(yùn)算符==來判斷值是否為null,我們也可以通過運(yùn)算符重載來實(shí)現(xiàn)該功能:

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);
}

接下來糖埋,我們來實(shí)現(xiàn)普通值類型和XfhNullable<T>之間的轉(zhuǎn)換:

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類型隱式轉(zhuǎn)換為XfhNullable<int>類型
    WriteLine(null_num == null);
    WriteLine(null_num.Value);
    int i = (int)null_num;//XfhNullable<int>類型強(qiáng)制轉(zhuǎn)換為int類型
    WriteLine(i);
}

獲取實(shí)例在運(yùn)行時(shí)的類型:

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

這個(gè)返回值不大友好宣吱,我們希望這里返回內(nèi)置的值類型,System.Int32瞳别,具體實(shí)現(xiàn)代碼如下:

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

結(jié)論:沒有可為空的值類型

至此凌节,我們已經(jīng)自定義了一個(gè)可為空的值類型XfhNullable<T>,通過以上代碼洒试,我們不難發(fā)現(xiàn)所謂可為空的值類型是不存在的倍奢,它是通過屬性HasValue來對null值進(jìn)行標(biāo)記的,其內(nèi)部通過字段innerValue(該字段對應(yīng)Nullable<T>中的value字段)來維護(hù)該類型的值垒棋,若被賦值為null則innerValue初始化為值類型的初始值卒煞。換句話說,Nullable<T>只是在邏輯層面上實(shí)現(xiàn)了把null賦值給值類型叼架,給我們一種值類型可為null的感覺畔裕。

最后說下可空值類型的裝箱與拆箱。
CLR在對Nullable<T>實(shí)例執(zhí)行裝箱操作時(shí)首先檢查它是否為null乖订,若是則CLR不裝箱任何東西而是直接返回null扮饶;若實(shí)例的值不是null則獲取該實(shí)例的值(Value屬性)并對這個(gè)值進(jìn)行裝箱操作。
拆箱時(shí)乍构,對于null則返回一個(gè)Nullable<T>()實(shí)例甜无,對于一個(gè)具體的數(shù)值,如5哥遮,則返回Nullable<T>(5)實(shí)例岂丘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市眠饮,隨后出現(xiàn)的幾起案子奥帘,更是在濱河造成了極大的恐慌,老刑警劉巖仪召,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寨蹋,死亡現(xiàn)場離奇詭異,居然都是意外死亡扔茅,警方通過查閱死者的電腦和手機(jī)已旧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咖摹,“玉大人评姨,你說我怎么就攤上這事难述∮┣纾” “怎么了吐句?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長店读。 經(jīng)常有香客問我嗦枢,道長,這世上最難降的妖魔是什么屯断? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任文虏,我火速辦了婚禮,結(jié)果婚禮上殖演,老公的妹妹穿的比我還像新娘氧秘。我一直安慰自己,他們只是感情好趴久,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布丸相。 她就那樣靜靜地躺著,像睡著了一般彼棍。 火紅的嫁衣襯著肌膚如雪灭忠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天座硕,我揣著相機(jī)與錄音弛作,去河邊找鬼。 笑死华匾,一個(gè)胖子當(dāng)著我的面吹牛映琳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜘拉,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刊头,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诸尽?” 一聲冷哼從身側(cè)響起原杂,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎您机,沒想到半個(gè)月后穿肄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡际看,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年咸产,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仲闽。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脑溢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屑彻,我是刑警寧澤验庙,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站社牲,受9級特大地震影響粪薛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搏恤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一违寿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧熟空,春花似錦藤巢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阱当,卻和暖如春俏扩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弊添。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工录淡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人油坝。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓嫉戚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親澈圈。 傳聞我的和親對象是個(gè)殘疾皇子彬檀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容