.NET中可空值類(lèi)型【Nullable<T>】實(shí)現(xiàn)原理
為了讓.Net中的值類(lèi)型可以賦值為null,微軟特地添加了Nullable<T>類(lèi)型,也可簡(jiǎn)寫(xiě)為T(mén)?。但是Nullable<T>自身是結(jié)構(gòu)體,也是值類(lèi)型,那么它是如何實(shí)現(xiàn)將null賦值給值類(lèi)型的呢?
下面通過(guò)自定義一個(gè)可空值類(lèi)型來(lái)講解Nullable<T>的實(shí)現(xiàn)原理。
自定義可空值類(lèi)型
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è)可空值類(lèi)型的結(jié)構(gòu)體大致功能已經(jīng)定義好了,下面我們來(lái)創(chuàng)建可空值類(lèi)型的實(shí)例來(lái)驗(yàn)證下。
using static System.Console; class Program { static void Main() { //使用結(jié)構(gòu)體默認(rèn)的無(wú)參構(gòu)造函數(shù)進(jìn)行實(shí)例化 XfhNullable<int> num = new XfhNullable<int>(); WriteLine(num.HasValue); WriteLine(null_num.GetValueOrDefault()); } }
可以看到,變量num并不含有值,調(diào)用GetValueOrDefault()則會(huì)獲取它的默認(rèn)值 0;
這時(shí)我們將null賦值給變量num會(huì)發(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>看作是普通值類(lèi)型而非可空值類(lèi)型,所以我們還要添加可空值類(lèi)型和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)了可空值類(lèi)型向XfhNullable<T>的隱式轉(zhuǎn)換,添加上面代碼之后發(fā)現(xiàn)編譯器不再報(bào)錯(cuò)。XfhNullable<T>已經(jīng)成為一個(gè)可為null的值類(lèi)型。
static void Main() { XfhNullable<int> null_num = null; WriteLine(null_num.HasValue); }
XfhNullable<T>中的屬性HasValue的作用就是標(biāo)記當(dāng)前類(lèi)型是否為null,若是則返回False,否則返回True。當(dāng)HasValue為False時(shí)調(diào)用該類(lèi)型的Value屬性則會(huì)拋出異常InvalidOperationException。但可調(diào)用GetValueOrDefault()方法來(lái)獲取類(lèi)型的默認(rèn)值。
Nullable<T>類(lèi)型可以通過(guò)運(yùn)算符==來(lái)判斷值是否為null,我們也可以通過(guò)運(yùn)算符重載來(lái)實(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); }
接下來(lái),我們來(lái)實(shí)現(xiàn)普通值類(lèi)型和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類(lèi)型隱式轉(zhuǎn)換為XfhNullable<int>類(lèi)型 WriteLine(null_num == null); WriteLine(null_num.Value); int i = (int)null_num;//XfhNullable<int>類(lèi)型強(qiáng)制轉(zhuǎn)換為int類(lèi)型 WriteLine(i); }
獲取實(shí)例在運(yùn)行時(shí)的類(lèi)型:
static void Main() { XfhNullable<int> null_num = 12; WriteLine(null_num.GetType()); }
這個(gè)返回值不大友好,我們希望這里返回內(nèi)置的值類(lèi)型,System.Int32,具體實(shí)現(xiàn)代碼如下:
//因?yàn)镺bject類(lèi)中的GetType方法不允許子類(lèi)重寫(xiě)(避免子類(lèi)隱藏自己的實(shí)際類(lèi)型) //所以這里使用關(guān)鍵字new來(lái)隱藏Object類(lèi)中的GetType方法 public new Type GetType() { return innerValue.GetType(); }
結(jié)論:沒(méi)有可為空的值類(lèi)型
至此,我們已經(jīng)自定義了一個(gè)可為空的值類(lèi)型XfhNullable<T>,通過(guò)以上代碼,我們不難發(fā)現(xiàn)所謂可為空的值類(lèi)型是不存在的,它是通過(guò)屬性HasValue來(lái)對(duì)null值進(jìn)行標(biāo)記的,其內(nèi)部通過(guò)字段innerValue(該字段對(duì)應(yīng)Nullable<T>中的value字段)來(lái)維護(hù)該類(lèi)型的值,若被賦值為null則innerValue初始化為值類(lèi)型的初始值。換句話說(shuō),Nullable<T>只是在邏輯層面上實(shí)現(xiàn)了把null賦值給值類(lèi)型,給我們一種值類(lèi)型可為null的感覺(jué)。
最后說(shuō)下可空值類(lèi)型的裝箱與拆箱。
CLR在對(duì)Nullable<T>實(shí)例執(zhí)行裝箱操作時(shí)首先檢查它是否為null,若是則CLR不裝箱任何東西而是直接返回null;若實(shí)例的值不是null則獲取該實(shí)例的值(Value屬性)并對(duì)這個(gè)值進(jìn)行裝箱操作。
拆箱時(shí),對(duì)于null則返回一個(gè)Nullable<T>()實(shí)例,對(duì)于一個(gè)具體的數(shù)值,如5,則返回Nullable<T>(5)實(shí)例。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
.NET截取指定長(zhǎng)度漢字超出部分以"..."代替 實(shí)例分享
.NET截取指定長(zhǎng)度漢字超出部分以"..."代替 實(shí)例分享,需要的朋友可以參考一下2013-06-06asp.net 更改gridview pageSize的方法
asp.net 更改gridview pageSize的方法,需要的朋友可以參考下。2011-07-07C#頁(yè)碼導(dǎo)航顯示及算法實(shí)現(xiàn)代碼
C#頁(yè)碼導(dǎo)航算法要求:頁(yè)數(shù)小于等于1時(shí)不顯示;頁(yè)數(shù)大于10時(shí),自動(dòng)縮短,需要的朋友可以了解下2012-12-12asp.net頁(yè)面與頁(yè)面之間傳參數(shù)值方法(post傳值和get傳值)
這篇文章主要介紹了asp.net頁(yè)面與頁(yè)面之間傳參數(shù)值方法,說(shuō)明了post傳值和get傳值的使用方法,需要的朋友可以參考下2014-02-02ASP.NET如何使用web服務(wù)的會(huì)話狀態(tài)
這篇文章主要介紹了ASP.NET如何使用web服務(wù)的會(huì)話狀態(tài),使用一個(gè)GridView中的會(huì)話對(duì)象來(lái)展示最近的計(jì)算結(jié)果,感興趣的小伙伴們可以參考一下2015-11-11asp.net中調(diào)用存儲(chǔ)過(guò)程的方法
這篇文章主要介紹了asp.net中調(diào)用存儲(chǔ)過(guò)程的方法,結(jié)合實(shí)例形式分析了存儲(chǔ)過(guò)程的建立與asp.net調(diào)用存儲(chǔ)過(guò)程的相關(guān)技巧,需要的朋友可以參考下2016-05-05ASP.NET 程序中刪除文件夾導(dǎo)致session失效問(wèn)題的解決辦法分享
這篇文章主要介紹了ASP.NET 程序中刪除文件夾導(dǎo)致session失效問(wèn)題的解決辦法分享,有需要的朋友可以參考一下2013-12-12.NET中RDLC循環(huán)處理數(shù)據(jù)的應(yīng)用分析
本篇文章介紹了,.NET中RDLC循環(huán)處理數(shù)據(jù)的應(yīng)用分析。需要的朋友參考下2013-05-05.net core利用orm如何操作mysql數(shù)據(jù)庫(kù)詳解
這篇文章主要給大家介紹了關(guān)于.net core利用orm如何操作mysql數(shù)據(jù)庫(kù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05ASP.NET Page函數(shù)調(diào)用順序解析
asp.net頁(yè)面事件執(zhí)行順序,需要的朋友可以參考下。2010-06-06