C# · 12月 20, 2021

四、可空类型Nullable<T>到底是什么鬼

值类型为什么不可以为空

首先我们都知道引用类型默认值都是null,而值类型的默认值都有非null。

为什么引用类型可以为空?因为引用类型变量都是保存一个对象的地址引用(),而引用类型值为null的时候是变量值指向了一个空引用()

那为什么值不能有空值呢?其实很简单,因为如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;”&gt;new</span><span style=”color: #000000;”&gt; Person(){ BeginTime </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1893-12-26</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;),EndTiem </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1976-09-09</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;)};Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;我今年</span><span style=”color: #800000;”&gt;”</span> + p1.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;);Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;毛爷爷活了</span><span style=”color: #800000;”&gt;”</span> + p2.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;);Console.ReadKey();

}

可空类型的实现

我们前面用到了  来表示可空时间类型,其实平时我们用得更多的是  直接在类型T后面加一个问号,这两种是等效的。多亏了微软的语法糖。

我们来看看  到底是何物。

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

不好意思,让大家失望了。前面我们就说过了,值类型是不可以赋值null的(结构也是值类型)。

怎么办!怎么办!不对啊,微软自己也是定义的结构,它怎么可以直接赋值null呢。(奇怪,奇怪,毕竟是人家微软自己搞得,可能得到了特殊的待遇吧)

可是,这样就让我们止步了吗?NO!我们都知道,看微软的IL(中间语言)的时候,就像脱了它的衣服一样,很多时候不明白的地方都可以看个究竟,下面我们就去脱衣服。

首先,我们用几种不同的方式给可空类型赋值。

Main(Sy<a href=”https://www.jb51.cc/tag/stem/” target=”_blank” class=”keywords”>stem</a>.Nullable</span><<span style=”color: #0000ff;”&gt;int</span>> number1 = <span style=”color: #0000ff;”&gt;null</span><span style=”color: #000000;”&gt;;Sy<a href=”https://www.jb51.cc/tag/stem/” target=”_blank” class=”keywords”>stem</a>.Nullable</span><<span style=”color: #0000ff;”&gt;int</span>> number2 = <span style=”color: #0000ff;”&gt;new</span> Sy<a href=”https://www.jb51.cc/tag/stem/” target=”_blank” class=”keywords”>stem</a>.Nullable<<span style=”color: #0000ff;”&gt;int</span>><span style=”color: #000000;”&gt;();Sy<a href=”https://www.jb51.cc/tag/stem/” target=”_blank” class=”keywords”>stem</a>.Nullable</span><<span style=”color: #0000ff;”&gt;int</span>> number3 = <span style=”color: #800080;”&gt;23</span><span style=”color: #000000;”&gt;;Sy<a href=”https://www.jb51.cc/tag/stem/” target=”_blank” class=”keywords”>stem</a>.Nullable</span><<span style=”color: #0000ff;”&gt;int</span>> number4 = <span style=”color: #0000ff;”&gt;new</span> Sy<a href=”https://www.jb51.cc/tag/stem/” target=”_blank” class=”keywords”>stem</a>.Nullable<<span style=”color: #0000ff;”&gt;int</span>>(<span style=”color: #800080;”&gt;88</span><span style=”color: #000000;”&gt;);Console.ReadKey();

}

然后用reflector看编译后的IL。

原来如此,可空类型的赋值直接等效于构造实例。赋null时其实就是调用空构造函数,有值时就就把值传入带参数的构造函数。(柳暗花明又一村。如此,我们是否可以接着上面截图中的  继续模拟可空类型呢?且继续往下看。)

MyNullable T : MyNullable(T value)._hasValue = ._value =</span><span style=”color: #0000ff;”&gt;private</span> <span style=”color: #0000ff;”&gt;bool</span><span style=”color: #000000;”&gt; _hasValue;</span><span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;bool</span> HasValue<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;是否不为空</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;”&gt;private</span><span style=”color: #000000;”&gt; T _value;</span><span style=”color: #0000ff;”&gt;public</span> T Value<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;值</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;”&gt;public</span> <span style=”color: #0000ff;”&gt;struct</span> MyNullable<T> <span style=”color: #0000ff;”&gt;where</span> T : <span style=”color: #0000ff;”&gt;struct</span><span style=”color: #000000;”&gt;{ </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;<a href=”https://www.jb51.cc/tag/cuowu/” target=”_blank” class=”keywords”>错误</a> 1 结构不能包含显式的无参数构造<a href=”https://www.jb51.cc/tag/hanshu/” target=”_blank” class=”keywords”>函数</a> </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;还好 bool<a href=”https://www.jb51.cc/tag/mo/” target=”_blank” class=”keywords”>默</a>认值就是false,所以这里<a href=”https://www.jb51.cc/tag/buxianshi/” target=”_blank” class=”keywords”>不显示</a>为 this._hasValue = false也不会有影响 </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;public MyNullable() </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;{ </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt; this._hasValue = false; </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;}</span> <span style=”color: #0000ff;”&gt;public</span> MyNullable(T value)<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;有参构造<a href=”https://www.jb51.cc/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;”&gt;private</span> <span style=”color: #0000ff;”&gt;bool</span><span style=”color: #000000;”&gt; _hasValue; </span><span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;bool</span> HasValue<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;是否不为空</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;”&gt;private</span><span style=”color: #000000;”&gt; T _value; </span><span style=”color: #0000ff;”&gt;public</span> T Value<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;值</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;”&gt;new</span><span style=”color: #000000;”&gt; Person() { BeginTime </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1893-12-26</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;),EndTiem </span>= <span style=”color: #0000ff;”&gt;new</span> MyNullable<DateTime>(DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1976-09-09</span><span style=”color: #800000;”&gt;”</span>))<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;这里使用MyNullable的有参构造<a href=”https://www.jb51.cc/tag/hanshu/” target=”_blank” class=”keywords”>函数</a></span>

<span style=”color: #000000;”> };

Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;我今年</span><span style=”color: #800000;”&gt;”</span> + p1.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;毛爷爷活了</span><span style=”color: #800000;”&gt;”</span> + p2.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); Console.ReadKey(); }}

}

和系统的可空类型得出了相同的结果。

总结可空类型是结构(也就是值类型)所以可空类型的null值和引用类型的null是不一样的。()

有同学问,怎么样才可以做到直接赋值呢?这个我也没有什么好的办法,或许需要编译器的支持。

以上内容都是胡说八道。希望能对您有那么一点点用处,感谢阅读。

(首发链接: )

============== 2016-06-05更新==============

上面我们提出了疑问“怎么样才可以做到直接赋值呢”,本来我是没有好的解决办法。这里要感谢我们的园友@给我提供了好的解决方案。

implicit()

MyNullable MyNullable

只需要在  MyNullable 中添加以上代码,就可以直接赋值了。()

完整代码如下:

<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;”&gt;public</span> <span style=”color: #0000ff;”&gt;struct</span> MyNullable<T> <span style=”color: #0000ff;”&gt;where</span> T : <span style=”color: #0000ff;”&gt;struct</span><span style=”color: #000000;”&gt;{ </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;<a href=”https://www.jb51.cc/tag/cuowu/” target=”_blank” class=”keywords”>错误</a> 1 结构不能包含显式的无参数构造<a href=”https://www.jb51.cc/tag/hanshu/” target=”_blank” class=”keywords”>函数</a> </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;还好 bool<a href=”https://www.jb51.cc/tag/mo/” target=”_blank” class=”keywords”>默</a>认值就是false,所以这里<a href=”https://www.jb51.cc/tag/buxianshi/” target=”_blank” class=”keywords”>不显示</a>为 this._hasValue = false也不会有影响 </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;public MyNullable() </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;{ </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt; this._hasValue = false; </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;} </span> <span style=”color: #0000ff;”&gt;public</span> MyNullable(T value)<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;有参构造<a href=”https://www.jb51.cc/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;”&gt;private</span> <span style=”color: #0000ff;”&gt;bool</span><span style=”color: #000000;”&gt; _hasValue; </span><span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;bool</span> HasValue<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;是否不为空</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;”&gt;private</span><span style=”color: #000000;”&gt; T _value; </span><span style=”color: #0000ff;”&gt;public</span> T Value<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;值</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;”&gt;public</span> <span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;implicit</span> <span style=”color: #0000ff;”&gt;operator</span> MyNullable<T><span style=”color: #000000;”&gt;(T value) { </span><span style=”color: #0000ff;”&gt;return</span> <span style=”color: #0000ff;”&gt;new</span> MyNullable<T><span style=”color: #000000;”&gt;(value); } }</span><span style=”color: #0000ff;”&gt;class</span><span style=”color: #000000;”&gt; Program{ </span><span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;void</span> Main(<span style=”color: #0000ff;”&gt;string</span><span style=”color: #000000;”&gt;[] args) { Person p1 </span>= <span style=”color: #0000ff;”&gt;new</span><span style=”color: #000000;”&gt; Person() { BeginTime </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1990-07-19</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;) }; Person p2 </span>= <span style=”color: #0000ff;”&gt;new</span><span style=”color: #000000;”&gt; Person() { BeginTime </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1893-12-26</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;),EndTiem </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1976-09-09</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;) </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;new MyNullable<DateTime>(DateTime.Parse(“1976-09-09″)) </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;这里使用MyNullable的有参构造<a href=”https://www.jb51.cc/tag/hanshu/” target=”_blank” class=”keywords”>函数</a></span>

<span style=”color: #000000;”> };

Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;我今年</span><span style=”color: #800000;”&gt;”</span> + p1.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;毛爷爷活了</span><span style=”color: #800000;”&gt;”</span> + p2.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); Console.ReadKey(); }}

}

如此,我们已经完成了自定义可空类型的直接赋值。但只是部分,如果想要赋值null呢?

同样还是出现了最开始的编译错误。我们想到既然上面的值赋值可以重新(),那null应该也可以啊()。

再加一个重载:

MyNullable( (value == MyNullable Exception(}

如此可以满足我们的需求了()。

可惜美中不足,如果给  赋值一个非空字符串时,要运行时才会报错()。不知道大神们可有解!!

虽然如此,能做到直接赋值还是让我小小激动了一把。为此,特意查了下关键字   ,又是让我小小激动了一把,我们不仅可以“重写”赋值,我们还可以“重写”+ – * / % & | ^ == != > = <=等运算符。

下面我们先来“重写”下自定义可空类型的比较()运算符。

==(MyNullable operand,MyNullable (!operand.HasValue && ! (operand.HasValue && <span style=”color: #008000;”>//<span style=”color: #008000;”>”重写”比较运算符
<span style=”color: #0000ff;”>public <span style=”color: #0000ff;”>static <span style=”color: #0000ff;”>bool <span style=”color: #0000ff;”>operator !=(MyNullable operand,MyNullable<span style=”color: #000000;”> operand2)
{
<span style=”color: #0000ff;”>return !(operand ==<span style=”color: #000000;”> operand2);
}

<div class=”cnblogs_code”>

Console.WriteLine( + (p1.EndTiem == + (p2.EndTiem == + (p1.EndTiem == DateTime.Parse( + (p2.EndTiem == DateTime.Parse(p1.EndTiem = DateTime.Parse(<span style=”color: #800000;”>”<span style=”color: #800000;”>2016-06-06<span style=”color: #800000;”>”<span style=”color: #000000;”>);
p2.EndTiem = <span style=”color: #0000ff;”>null<span style=”color: #000000;”>;
Console.WriteLine();
Console.WriteLine(<span style=”color: #800000;”>”<span style=”color: #800000;”>赋值 p1.EndTiem = DateTime.Parse(2016-06-06) p2.EndTiem = null 后:<span style=”color: #800000;”>”<span style=”color: #000000;”>);
Console.WriteLine(<span style=”color: #800000;”>”<span style=”color: #800000;”>p1.EndTiem == null,<span style=”color: #800000;”>” + (p2.EndTiem == <span style=”color: #0000ff;”>null<span style=”color: #000000;”>).ToString());
Console.WriteLine(<span style=”color: #800000;”>”<span style=”color: #800000;”>p1.EndTiem == DateTime.Parse(2016-06-06),<span style=”color: #800000;”>” + (p1.EndTiem == DateTime.Parse(<span style=”color: #800000;”>”<span style=”color: #800000;”>2016-06-06<span style=”color: #800000;”>”<span style=”color: #000000;”>)).ToString());
Console.WriteLine(<span style=”color: #800000;”>”<span style=”color: #800000;”>p2.EndTiem == DateTime.Parse(2016-06-06),<span style=”color: #800000;”>” + (p2.EndTiem == DateTime.Parse(<span style=”color: #800000;”>”<span style=”color: #800000;”>2016-06-06<span style=”color: #800000;”>”)).ToString());

结果完全符合!

完整代码如下:

<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;”&gt;public</span> <span style=”color: #0000ff;”&gt;struct</span> MyNullable<T> <span style=”color: #0000ff;”&gt;where</span> T : <span style=”color: #0000ff;”&gt;struct</span><span style=”color: #000000;”&gt;{ </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;<a href=”https://www.jb51.cc/tag/cuowu/” target=”_blank” class=”keywords”>错误</a> 1 结构不能包含显式的无参数构造<a href=”https://www.jb51.cc/tag/hanshu/” target=”_blank” class=”keywords”>函数</a> </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;还好 bool<a href=”https://www.jb51.cc/tag/mo/” target=”_blank” class=”keywords”>默</a>认值就是false,所以这里<a href=”https://www.jb51.cc/tag/buxianshi/” target=”_blank” class=”keywords”>不显示</a>为 this._hasValue = false也不会有影响 </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;public MyNullable() </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;{ </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt; this._hasValue = false; </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;} </span> <span style=”color: #0000ff;”&gt;public</span> MyNullable(T value)<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;有参构造<a href=”https://www.jb51.cc/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;”&gt;private</span> <span style=”color: #0000ff;”&gt;bool</span><span style=”color: #000000;”&gt; _hasValue; </span><span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;bool</span> HasValue<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;是否不为空</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;”&gt;private</span><span style=”color: #000000;”&gt; T _value; </span><span style=”color: #0000ff;”&gt;public</span> T Value<span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;值</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: #008000;”&gt;//</span><span style=”color: #008000;”&gt;隐式转换</span> <span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;implicit</span> <span style=”color: #0000ff;”&gt;operator</span> MyNullable<T><span style=”color: #000000;”&gt;(T value) { </span><span style=”color: #0000ff;”&gt;return</span> <span style=”color: #0000ff;”&gt;new</span> MyNullable<T><span style=”color: #000000;”&gt;(value); } </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;隐式转换</span> <span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;implicit</span> <span style=”color: #0000ff;”&gt;operator</span> MyNullable<T>(<span style=”color: #0000ff;”&gt;string</span><span style=”color: #000000;”&gt; value) { </span><span style=”color: #0000ff;”&gt;if</span> (value == <span style=”color: #0000ff;”&gt;null</span><span style=”color: #000000;”&gt;) </span><span style=”color: #0000ff;”&gt;return</span> <span style=”color: #0000ff;”&gt;new</span> MyNullable<T><span style=”color: #000000;”&gt;(); </span><span style=”color: #0000ff;”&gt;throw</span> <span style=”color: #0000ff;”&gt;new</span> Exception(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;赋值右边不能为字符串</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;这里不知道是否可以在编译期间抛出<a href=”https://www.jb51.cc/tag/cuowu/” target=”_blank” class=”keywords”>错误</a>(或者怎样限制只能传null)</span>

<span style=”color: #000000;”> }

</span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;”重写”比较运算符</span> <span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;bool</span> <span style=”color: #0000ff;”&gt;operator</span> ==(MyNullable<T> operand,MyNullable<T><span style=”color: #000000;”&gt; operand2) { </span><span style=”color: #0000ff;”&gt;if</span> (!operand.HasValue &amp;&amp; !<span style=”color: #000000;”&gt;operand2.HasValue) { </span><span style=”color: #0000ff;”&gt;return</span> <span style=”color: #0000ff;”&gt;true</span><span style=”color: #000000;”&gt;; } </span><span style=”color: #0000ff;”&gt;else</span> <span style=”color: #0000ff;”&gt;if</span> (operand.HasValue &amp;&amp;<span style=”color: #000000;”&gt; operand2.HasValue) { </span><span style=”color: #0000ff;”&gt;if</span><span style=”color: #000000;”&gt; (operand2.Value.Equals(operand.Value)) { </span><span style=”color: #0000ff;”&gt;return</span> <span style=”color: #0000ff;”&gt;true</span><span style=”color: #000000;”&gt;; } } </span><span style=”color: #0000ff;”&gt;return</span> <span style=”color: #0000ff;”&gt;false</span><span style=”color: #000000;”&gt;; } </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;”重写”比较运算符</span> <span style=”color: #0000ff;”&gt;public</span> <span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;bool</span> <span style=”color: #0000ff;”&gt;operator</span> !=(MyNullable<T> operand,MyNullable<T><span style=”color: #000000;”&gt; operand2) { </span><span style=”color: #0000ff;”&gt;return</span> !(operand ==<span style=”color: #000000;”&gt; operand2); }}</span><span style=”color: #0000ff;”&gt;class</span><span style=”color: #000000;”&gt; Program{ </span><span style=”color: #0000ff;”&gt;static</span> <span style=”color: #0000ff;”&gt;void</span> Main(<span style=”color: #0000ff;”&gt;string</span><span style=”color: #000000;”&gt;[] args) { Person p1 </span>= <span style=”color: #0000ff;”&gt;new</span><span style=”color: #000000;”&gt; Person() { BeginTime </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1990-07-19</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;) }; Person p2 </span>= <span style=”color: #0000ff;”&gt;new</span><span style=”color: #000000;”&gt; Person() { BeginTime </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1893-12-26</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;),EndTiem </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1976-09-09</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;) </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;new MyNullable<DateTime>(DateTime.Parse(“1976-09-09″)) </span><span style=”color: #008000;”&gt;//</span><span style=”color: #008000;”&gt;这里使用MyNullable的有参构造<a href=”https://www.jb51.cc/tag/hanshu/” target=”_blank” class=”keywords”>函数</a></span>

<span style=”color: #000000;”> };

Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;我今年</span><span style=”color: #800000;”&gt;”</span> + p1.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;毛爷爷活了</span><span style=”color: #800000;”&gt;”</span> + p2.Age + <span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;岁。</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); Console.WriteLine(); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;p1.EndTiem == null,</span><span style=”color: #800000;”&gt;”</span> + (p1.EndTiem == <span style=”color: #0000ff;”&gt;null</span><span style=”color: #000000;”&gt;).ToString()); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;p2.EndTiem == null,</span><span style=”color: #800000;”&gt;”</span> + (p2.EndTiem == <span style=”color: #0000ff;”&gt;null</span><span style=”color: #000000;”&gt;).ToString()); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;p1.EndTiem == DateTime.Parse(1976-09-09),</span><span style=”color: #800000;”&gt;”</span> + (p1.EndTiem == DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1976-09-09</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;)).ToString()); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;p2.EndTiem == DateTime.Parse(1976-09-09),</span><span style=”color: #800000;”&gt;”</span> + (p2.EndTiem == DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;1976-09-09</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;)).ToString()); p1.EndTiem </span>= DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;2016-06-06</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); p2.EndTiem </span>= <span style=”color: #0000ff;”&gt;null</span><span style=”color: #000000;”&gt;; Console.WriteLine(); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;赋值 p1.EndTiem = DateTime.Parse(2016-06-06) p2.EndTiem = null 后:</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;p1.EndTiem == null,</span><span style=”color: #800000;”&gt;”</span> + (p2.EndTiem == <span style=”color: #0000ff;”&gt;null</span><span style=”color: #000000;”&gt;).ToString()); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;p1.EndTiem == DateTime.Parse(2016-06-06),</span><span style=”color: #800000;”&gt;”</span> + (p1.EndTiem == DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;2016-06-06</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;)).ToString()); Console.WriteLine(</span><span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;p2.EndTiem == DateTime.Parse(2016-06-06),</span><span style=”color: #800000;”&gt;”</span> + (p2.EndTiem == DateTime.Parse(<span style=”color: #800000;”&gt;”</span><span style=”color: #800000;”&gt;2016-06-06</span><span style=”color: #800000;”&gt;”</span><span style=”color: #000000;”&gt;)).ToString()); Console.ReadKey(); }}

}

转换关键字:operator、explicit与implicit解析资料:

大家还可以玩出更多的花样!!!

本文已同步至《》