先贴下Nullable<T>简单实现:
1 [Serializable]
2 public struct Nulllable<T> where T : struct
3 {
4 private Boolean hasValue = false;
5 internal T value = default(T);
6 public void Nullable(T value)
7 {
8 this.value = value;
9 this.hasValue = true;
10 }
11 public Boolean HashValue
12 {
13 get { return this.hasValue; }
14 }
15 public T Value
16 {
17 get
18 {
19 if (!hasValue)
20 {
21 throw new InvalidCastException("nullable Object mush have a value");
22 }
23 return value;
24 }
25 }
26 public T GetValueOrDefault()
27 {
28 return value;
29 }
30 public T GetValueOrDefault(T defaultValue)
31 {
32 if (!HashValue)
33 return defaultValue;
34 return value;
35 }
36 public override bool Equals(object other)
37 {
38 if (!HashValue)
39 return (other == null);
40 if (other == null)
41 return false;
42 return value.Equals(other);
43 }
44 public override int GetHashCode()
45 {
46 if (!HashValue)
47 return 0;
48 return value.GetHashCode();
49 }
50 public override string ToString()
51 {
52 if (!HashValue) return "";
53 return value.ToString();
54 }
55 public static implicit operator Nullable<T>(T value)
56 {
57 return new Nullable<T>(value);
58 }
59 public static explicit operator T(Nullable<T> value)
60 {
61 return value.Value;
62 }
63
64 }
65 }
大家可以看到 public struct Nulllable<T> where T : struct
Nulllable<T>为值类型 同样 T 也为值类型 理论上 Nulllable<T> 不可能跟null扯上关系
那为什么 Nulllable<Int32> temp = null; 那这个null 去那了呢?
下面就通过IL来看看到底发生了什么吧!
第一次:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Nullable<Int32> temp = null;
}
}
}
对应的IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 10 (0xa)
.maxstack 1
.locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> temp)
IL_0000: nop
IL_0001: ldloca.s temp
IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0009: ret
} // end of method Program::Main
从这里可以看到他这里只是往栈里压了一个 压了一个变量 大家完全可以理解为 Nullable<Int32> temp;
this.value = 0; 因为default(T)
this.hasValue = false;
接下来 :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Nullable<Int32> temp = 22;
}
}
}
IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 12 (0xc)
.maxstack 2
.locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> temp)
IL_0000: nop
IL_0001: ldloca.s temp
IL_0003: ldc.i4.s 22
IL_0005: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_000a: nop
IL_000b: ret
} // end of method Program::Main
看到了没 区别在: IL_0005: call instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
他执行了构造函数;
此时:
public void Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
this.value = 22;
this.hasValue = true;
接下来:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Nullable<Int32> temp = null;
if (temp == null)
{
}
}
}
}
IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 23 (0x17)
.maxstack 1
.locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> temp,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: ldloca.s temp
IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0009: ldloca.s temp
IL_000b: call instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: brtrue.s IL_0016
IL_0014: nop
IL_0015: nop
IL_0016: ret
} // end of method Program::Main
这里的IF执行的是什么呢?
他首先会去检查里面有没有值 IL_000b: call instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
看到这里大家有没有发现这个null那都没用到!没错 其实这里c#编辑器跟我们玩了个魔术 !
看到这里肯定有人会这么写代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Nullable<Int32> temp = new Nullable<Int32>(); //相当于 Nullable<Int32> temp = null;
Nullable<Int32> temp1 = new Nullable<Int32>(22); //相当于 Nullable<Int32> temp = 22;
}
}
}
没错这也是完全正确的!
编译器成功骗过了我们追求真理程序猿!