可空值类型:具有以下特性:
1.可空值类型名为Nullable<T>或者T?,其中T被约束成struct。
2.Nullable<T>中存在hasValue和value两个字段。并且在无参构造函数中hasValue为false,value为default(T);在有参构造函数中hasValue为true,value为传入的非null值。
3.Nullable<T>本身是值类型,它的实例仍然可以在栈上,而且实例的大小和T基本一样,只是多了一个hasValue字段。
4.T和Nullable<T>之间可以进行相互转换(T一致)或者转型(T不一致)。参考代码如下所示:
// 从Int32隐式转换成Nullable<Int32>
Int32? a = 5; // 等价于Int32? a = new Int32?(5);
// 将null隐式转换成Nullable<Int32>
Int32? b = null; // 等价于Int32? b = new Int32?();
// 将Nullable<Int32>显示转换成Int32
Int32 c = (Int32)a;
// 将Int32隐式转型成Nullable<Double>
Double? d = 5;
// 将将Nullable<Int32>显示转型成Double
Double e = (Double)a;
5.可以对Nullable<T>实例应用操作符。如下所示:
1>.一元操作符(+,++,-,–,!,~):操作数为null,结果就为null。
2>.二元操作符(+,-,*,/,%,&,|,^,<<,>>):如果两个操作数都是null,结果就为null。如果两个操作数都不为null。操作符就跟平常一样工作。如果两个操作数有一个为null,当不是对Boolean?类型操作数做&和|操作时,结果就为null;否则结果如下表所示:
操作数 | true | false | null |
---|---|---|---|
true | &=true |=true | &=false |=true | &=null |=true |
false | &=false |=true | &=false |=false | &=false |=null |
null | &=null |=true | &=false |=null | &=null |=null |
3>.相等性操作符(==,!=):如果两个操作数都是null,结果为true。如果一个操作数为null,结果为false。如果两个操作数都不为null,就比较值。
4>.关系操作符(<,<=,>,>=):两个操作数任何一个为null,结果就是false。两个操作数都不是null,就比较值。
5>.操作Nullable<T>实例会生成大量代码,而且操作速度会慢于操作T实例。所以建议自己重写T中的上述操作符,这样在Nullable<T>实例时,最终操作的是自己重写的操作符。
空接合操作符:在C#中用??表示。当左边操作数的值不为null时,就返回该操作数的值;否则就返回右边操作数的值。
CLR对可空值类型的特殊支持:如下所示:
1.当CLR对Nullable<T>实例进行装箱时,会检查Nullable<T>实例是否为null。如果是的话就不装箱任何东西并返回null;否则就将Nullable<T>实例中的value字段值进行装箱并返回装箱后的引用。
2.CLR将已装箱实例拆箱为Nullable<T>实例时,会检查已装箱实例的引用是否为nul。如果是的话就不拆箱任何东西并返回null;否则就将已装箱实例进行拆箱并返回拆箱后的值。
3.对Nullable<T>实例调用GetType函数时,会检查Nullable<T>实例是否为null。如果是的话就抛出异常;否则就返回Nullable<T>实例当中value字段的类型字符串。
4.对Nullable<T>实例调用接口函数时,会检查Nullable<T>实例是否为null。如果是的话就抛出异常;否则就调用Nullable<T>实例当中value字段的接口函数。