前端之家收集整理的这篇文章主要介绍了
四、可空类型Nullable<T>到底是什么鬼,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
值类型为什么不可以为空
首先我们都知道引用类型默认值都是null,而值类型的默认值都有非null。

为什么引用类型可以为空?因为引用类型变量都是保存一个对象的地址引用(页面),而引用类型值为null的时候是变量值指向了一个空引用()
5020288c69b2b5532357b.png" alt="">
那为什么值不能有空值呢?其实很简单,因为如int值范围是-2147483648到2147483647。其中根本就没有给null值留那么一个位置。

我们为什么需要用到可空类型
举个栗子吧,我们定义一个人(Person),它有三个属性出生日期(BeginTime)、死亡日期(EndTime)、年龄(Age)。
如果这个人还健在人世,请问怎么给死亡日期赋值?有人很聪明说“为空啊”。是的,这就是我们的需求。
微软在C#2.0的时候就为我们引入了可null值类型( ),那么下面来定义Person类。
DateTime BeginTime { ;
System.Nullable EndTiem { ;
(EndTiem.HasValue)
(EndTiem.Value -
(DateTime.Now - }
这样,我们就可以很容易获得一个人的年龄了。
Main(= = DateTime.Parse(Person p2 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Person()
{
BeginTime </span>= DateTime.Parse(<span style="color: #800000;">"</span><span style="color: #800000;">1893-12-26</span><span style="color: #800000;">"</span><span style="color: #000000;">),EndTiem </span>= DateTime.Parse(<span style="color: #800000;">"</span><span style="color: #800000;">1976-09-09</span><span style="color: #800000;">"</span><span style="color: #000000;">)
};
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">我今年</span><span style="color: #800000;">"</span> + p1.Age + <span style="color: #800000;">"</span><span style="color: #800000;">岁。</span><span style="color: #800000;">"</span><span style="color: #000000;">);
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">毛爷爷活了</span><span style="color: #800000;">"</span> + p2.Age + <span style="color: #800000;">"</span><span style="color: #800000;">岁。</span><span style="color: #800000;">"</span><span style="color: #000000;">);
Console.ReadKey();
}
可空类型的实现
我们前面用到了 来表示可空时间类型,其实平时我们用得更多的是 直接在类型T后面加一个问号,这两种是等效的。多亏了微软的语法糖。
我们来看看 到底是何物。

搜噶,原来是一个结构。还看到了我们属性的 HasValue和Value属性。原来竟这般简单。一个结构两个属性,一个存值,一个存是否有值。那么下面我们也来试试吧。

不好意思,让大家失望了。前面我们就说过了,值类型是不可以赋值null的(结构也是值类型)。
怎么办!怎么办!不对啊,微软自己也是定义的结构,它怎么可以直接赋值null呢。(奇怪,奇怪,毕竟是人家微软自己搞得,可能得到了特殊的待遇吧)
可是,这样就让我们止步了吗?NO!我们都知道,看微软的IL(中间语言)的时候,就像脱了它的衣服一样,很多时候不明白的地方都可以看个究竟,下面我们就去脱衣服。
首先,我们用几种不同的方式给可空类型赋值。
Main(System.Nullable</span><<span style="color: #0000ff;">int</span>> number1 = <span style="color: #0000ff;">null</span><span style="color: #000000;">;
System.Nullable</span><<span style="color: #0000ff;">int</span>> number2 = <span style="color: #0000ff;">new</span> System.Nullable<<span style="color: #0000ff;">int</span>><span style="color: #000000;">();
System.Nullable</span><<span style="color: #0000ff;">int</span>> number3 = <span style="color: #800080;">23</span><span style="color: #000000;">;
System.Nullable</span><<span style="color: #0000ff;">int</span>> number4 = <span style="color: #0000ff;">new</span> System.Nullable<<span style="color: #0000ff;">int</span>>(<span style="color: #800080;">88</span><span style="color: #000000;">);
Console.ReadKey();
}
然后用reflector看编译后的IL。

原来如此,可空类型的赋值直接等效于构造实例。赋null时其实就是调用空构造函数,有值时就就把值传入带参数的构造函数。(柳暗花明又一村。如此,我们是否可以接着上面截图中的 继续模拟可空类型呢?且继续往下看。)
MyNullable T : 错误 1 结构不能包含显式的无参数构造函数
不显示为 this._hasValue = false也不会有影响
MyNullable(T value)函数
._hasValue = ._value =</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> _hasValue;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span> HasValue<span style="color: #008000;">//</span><span style="color: #008000;">是否不为空</span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">get { <span style="color: #0000ff;">return<span style="color: #000000;"> _hasValue; }
}
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> T _value;
</span><span style="color: #0000ff;">public</span> T Value<span style="color: #008000;">//</span><span style="color: #008000;">值</span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">get<span style="color: #000000;">
{
<span style="color: #0000ff;">if (!<span style="color: #0000ff;">this._hasValue)<span style="color: #008000;">//<span style="color: #008000;">如没有值,还访问就抛出异常
<span style="color: #000000;"> {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new Exception(<span style="color: #800000;">"<span style="color: #800000;"> 可为空的对象必须具有一个值<span style="color: #800000;">"<span style="color: #000000;">);
}
<span style="color: #0000ff;">return<span style="color: #000000;"> _value;
}
}
}
哟西,基本上已经模拟出了可空类型出来的。(函数的方式来使用自定义的可空类型)。
全部代码如下:
<span style="color: #0000ff;">namespace<span style="color: #000000;"> 可空类型
{
<span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> Person
{
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #808080;">///<span style="color: #008000;"> 出生日期
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #0000ff;">public DateTime BeginTime { <span style="color: #0000ff;">get; <span style="color: #0000ff;">set<span style="color: #000000;">; }
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #808080;">///<span style="color: #008000;"> 死亡日期
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #0000ff;">public MyNullable
EndTiem { <span style="color: #0000ff;">get; <span style="color: #0000ff;">set; } <span style="color: #008000;">//<span style="color: #008000;">这里改用MyNullable
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #808080;">///<span style="color: #008000;"> 年龄
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #0000ff;">public <span style="color: #0000ff;">double<span style="color: #000000;"> Age
{
<span style="color: #0000ff;">get<span style="color: #000000;">
{
<span style="color: #0000ff;">if (EndTiem.HasValue)<span style="color: #008000;">//<span style="color: #008000;">如果挂了(如果有值,证明死了)
<span style="color: #000000;"> {
<span style="color: #0000ff;">return (EndTiem.Value - BeginTime).Days / <span style="color: #800080;">365<span style="color: #000000;">;
}
<span style="color: #0000ff;">else<span style="color: #008000;">//<span style="color: #008000;">还没挂
<span style="color: #000000;"> {
<span style="color: #0000ff;">return (DateTime.Now - BeginTime).Days / <span style="color: #800080;">365<span style="color: #000000;">;
}
}
}
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">struct</span> MyNullable<T> <span style="color: #0000ff;">where</span> T : <span style="color: #0000ff;">struct</span><span style="color: #000000;">
{
</span><span style="color: #008000;">//</span><span style="color: #008000;"><a href="/tag/cuowu/" target="_blank" class="keywords">错误</a> 1 结构不能包含显式的无参数构造<a href="/tag/hanshu/" target="_blank" class="keywords">函数</a>
</span><span style="color: #008000;">//</span><span style="color: #008000;">还好 bool默认值就是false,所以这里<a href="/tag/buxianshi/" target="_blank" class="keywords">不显示</a>为 this._hasValue = false也不会有影响
</span><span style="color: #008000;">//</span><span style="color: #008000;">public MyNullable()
</span><span style="color: #008000;">//</span><span style="color: #008000;">{
</span><span style="color: #008000;">//</span><span style="color: #008000;"> this._hasValue = false;
</span><span style="color: #008000;">//</span><span style="color: #008000;">}</span>
<span style="color: #0000ff;">public</span> MyNullable(T value)<span style="color: #008000;">//</span><span style="color: #008000;">有参构造<a href="/tag/hanshu/" target="_blank" class="keywords">函数</a></span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">this._hasValue = <span style="color: #0000ff;">true<span style="color: #000000;">;
<span style="color: #0000ff;">this._value =<span style="color: #000000;"> value;
}
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> _hasValue;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span> HasValue<span style="color: #008000;">//</span><span style="color: #008000;">是否不为空</span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">get { <span style="color: #0000ff;">return<span style="color: #000000;"> _hasValue; }
}
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> T _value;
</span><span style="color: #0000ff;">public</span> T Value<span style="color: #008000;">//</span><span style="color: #008000;">值</span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">get<span style="color: #000000;">
{
<span style="color: #0000ff;">if (!<span style="color: #0000ff;">this._hasValue)<span style="color: #008000;">//<span style="color: #008000;">如没有值,还访问就抛出异常
<span style="color: #000000;"> {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new Exception(<span style="color: #800000;">"<span style="color: #800000;"> 可为空的对象必须具有一个值<span style="color: #800000;">"<span style="color: #000000;">);
}
<span style="color: #0000ff;">return<span style="color: #000000;"> _value;
}
}
}
<span style="color: #0000ff;">class<span style="color: #000000;"> Program
{
<span style="color: #0000ff;">static <span style="color: #0000ff;">void Main(<span style="color: #0000ff;">string<span style="color: #000000;">[] args)
{
Person p1 = <span style="color: #0000ff;">new<span style="color: #000000;"> Person()
{
BeginTime = DateTime.Parse(<span style="color: #800000;">"<span style="color: #800000;">1990-07-19<span style="color: #800000;">"<span style="color: #000000;">)
};
Person p2 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Person()
{
BeginTime </span>= DateTime.Parse(<span style="color: #800000;">"</span><span style="color: #800000;">1893-12-26</span><span style="color: #800000;">"</span><span style="color: #000000;">),EndTiem </span>= <span style="color: #0000ff;">new</span> MyNullable<DateTime>(DateTime.Parse(<span style="color: #800000;">"</span><span style="color: #800000;">1976-09-09</span><span style="color: #800000;">"</span>))<span style="color: #008000;">//</span><span style="color: #008000;">这里使用MyNullable的有参构造<a href="/tag/hanshu/" target="_blank" class="keywords">函数</a></span>
<span style="color: #000000;"> };
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">我今年</span><span style="color: #800000;">"</span> + p1.Age + <span style="color: #800000;">"</span><span style="color: #800000;">岁。</span><span style="color: #800000;">"</span><span style="color: #000000;">);
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">毛爷爷活了</span><span style="color: #800000;">"</span> + p2.Age + <span style="color: #800000;">"</span><span style="color: #800000;">岁。</span><span style="color: #800000;">"</span><span style="color: #000000;">);
Console.ReadKey();
}
}
}
<span style="color: #0000ff;">namespace<span style="color: #000000;"> test
{
<span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> Person
{
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #808080;">///<span style="color: #008000;"> 出生日期
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #0000ff;">public DateTime BeginTime { <span style="color: #0000ff;">get; <span style="color: #0000ff;">set<span style="color: #000000;">; }
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #808080;">///<span style="color: #008000;"> 死亡日期
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #0000ff;">public MyNullable
EndTiem { <span style="color: #0000ff;">get; <span style="color: #0000ff;">set; } <span style="color: #008000;">//<span style="color: #008000;">这里改用MyNullable
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #808080;">///<span style="color: #008000;"> 年龄
<span style="color: #808080;">/// <span style="color: #808080;">
<span style="color: #0000ff;">public <span style="color: #0000ff;">double<span style="color: #000000;"> Age
{
<span style="color: #0000ff;">get<span style="color: #000000;">
{
<span style="color: #0000ff;">if (EndTiem.HasValue)<span style="color: #008000;">//<span style="color: #008000;">如果挂了(如果有值,证明死了)
<span style="color: #000000;"> {
<span style="color: #0000ff;">return (EndTiem.Value - BeginTime).Days / <span style="color: #800080;">365<span style="color: #000000;">;
}
<span style="color: #0000ff;">else<span style="color: #008000;">//<span style="color: #008000;">还没挂
<span style="color: #000000;"> {
<span style="color: #0000ff;">return (DateTime.Now - BeginTime).Days / <span style="color: #800080;">365<span style="color: #000000;">;
}
}
}
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">struct</span> MyNullable<T> <span style="color: #0000ff;">where</span> T : <span style="color: #0000ff;">struct</span><span style="color: #000000;">
{
</span><span style="color: #008000;">//</span><span style="color: #008000;"><a href="/tag/cuowu/" target="_blank" class="keywords">错误</a> 1 结构不能包含显式的无参数构造<a href="/tag/hanshu/" target="_blank" class="keywords">函数</a>
</span><span style="color: #008000;">//</span><span style="color: #008000;">还好 bool默认值就是false,所以这里<a href="/tag/buxianshi/" target="_blank" class="keywords">不显示</a>为 this._hasValue = false也不会有影响
</span><span style="color: #008000;">//</span><span style="color: #008000;">public MyNullable()
</span><span style="color: #008000;">//</span><span style="color: #008000;">{
</span><span style="color: #008000;">//</span><span style="color: #008000;"> this._hasValue = false;
</span><span style="color: #008000;">//</span><span style="color: #008000;">} </span>
<span style="color: #0000ff;">public</span> MyNullable(T value)<span style="color: #008000;">//</span><span style="color: #008000;">有参构造<a href="/tag/hanshu/" target="_blank" class="keywords">函数</a></span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">this._hasValue = <span style="color: #0000ff;">true<span style="color: #000000;">;
<span style="color: #0000ff;">this._value =<span style="color: #000000;"> value;
}
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> _hasValue;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span> HasValue<span style="color: #008000;">//</span><span style="color: #008000;">是否不为空</span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">get { <span style="color: #0000ff;">return<span style="color: #000000;"> _hasValue; }
}
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> T _value;
</span><span style="color: #0000ff;">public</span> T Value<span style="color: #008000;">//</span><span style="color: #008000;">值</span>
<span style="color: #000000;"> {
<span style="color: #0000ff;">get<span style="color: #000000;">
{
<span style="color: #0000ff;">if (!<span style="color: #0000ff;">this._hasValue)<span style="color: #008000;">//<span style="color: #008000;">如没有值,还访问就抛出异常
<span style="color: #000000;"> {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new InvalidOperationException(<span style="color: #800000;">"<span style="color: #800000;"> 可为空的对象必须具有一个值<span style="color: #800000;">"<span style="color: #000000;">);
}
<span style="color: #0000ff;">return<span style="color: #000000;"> _value;
}
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">implicit</span> <span style="color: #0000ff;">operator</span> MyNullable<T><span style="color: #000000;">(T value)
{
</span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> MyNullable<T><span style="color: #000000;">(value);
}
}
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Program
{
</span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> Main(<span style="color: #0000ff;">string</span><span style="color: #000000;">[] args)
{
Person p1 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Person()
{
BeginTime </span>= DateTime.Parse(<span style="color: #800000;">"</span><span style="color: #800000;">1990-07-19</span><span style="color: #800000;">"</span><span style="color: #000000;">)
};
Person p2 </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Person()
{
BeginTime </span>= DateTime.Parse(<span style="color: #800000;">"</span><span style="color: #800000;">1893-12-26</span><span style="color: #800000;">"</span><span style="color: #000000;">),EndTiem </span>= DateTime.Parse(<span style="color: #800000;">"</span><span style="color: #800000;">1976-09-09</span><span style="color: #800000;">"</span><span style="color: #000000;">)
</span><span style="color: #008000;">//</span><span style="color: #008000;">new MyNullable<DateTime>(DateTime.Parse("1976-09-09"))
</span><span style="color: #008000;">//</span><span style="color: #008000;">这里使用MyNullable的有参构造<a href="/tag/hanshu/" target="_blank" class="keywords">函数</a></span>
<span style="color: #000000;"> };
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">我今年</span><span style="color: #800000;">"</span> + p1.Age + <span style="color: #800000;">"</span><span style="color: #800000;">岁。</span><span style="color: #800000;">"</span><span style="color: #000000;">);
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">毛爷爷活了</span><span style="color: #800000;">"</span> + p2.Age + <span style="color: #800000;">"</span><span style="color: #800000;">岁。</span><span style="color: #800000;">"</span><span style="color: #000000;">);
Console.ReadKey();
}
}
}