為了讓.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)HasValue
為False
時(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í)例岂丘。