可空值类型和?运算符
谈到运算符,大家一定很熟悉,但是对所有的运算符都能掌握吗? 看了下面代码再回答。
1 Nullable<Int32> count = 3; 2 3 int? i = 1; 4 5 bool? flag = false; 6 7 bool hasValue = flag ?? false;
相信在大多数情况下,对第三行和第7行的使用方法比较少。他们究竟代表啥含义,int? 和 int 有什么区别, “??”运算符是什么意思?
这个问题就需要提到C#中可空值类型(Nullable),顾名思义,他就是一个可以为null的值类型,嗯,是为null的值类型,没听错。不是值类型不能为空,为啥要引入一个可为空的值类型呢?这就需要涉及到实际开发中碰到的问题来看。例如,设计一个数据库时,开奖一个列的数据类型定义为32位整数,并且可以映射到FCL的Int32。但是有些情况下,数据库列为空,这样从数据库读出来的数据在映射为FCL的数据类型时该怎么办?CLR中不能讲一个Int32表示成null。
这样引入可空值类型就是有必要的.上面代码中第一行就是可空值类型的使用,他是一个泛型类
public struct Nullable<T> where T : struct { }
T 被限定为struct,即值类型,如果是引用类型就没必要使用可空值类型,可以直接赋值为null。那么第三行的代码是怎么回事呢? 这就是用"?"来表示的可空值类型,Nullable<Int32> 等同于 Int32? ,个人观点来说Nullable<T>这种写法更具有直观性。
从上面看出可控制类型是个类,因此Nullable<Int32>和Int32 是不能直接来用=赋值的(编译器报错),那么他们在使用过程中就进行类型转换,例如
1 int x = 9; 2 int? a = x; //正确 3 int? z = (int?)x;//正确 4 //int y = a; //Error Cannot implicitly convert type 'int?' to 'int' 5 int y = (int)a;//正确
第二行代码中赋值是进行了隐式类型转换,编译器没有报错,第三行进行显式类型转换也没有问题。但是第四行就会报错,需要强制类型转换。那么有类型转换,那么肯定会联想到装箱和拆箱,那么编译器是怎么装箱和拆箱的呢?
装箱:当CLR对一个Nullable<T>实例进行装箱时,首先检查他是否为null,如果是,CRL不进行任何操作,直接返回null。如果实例不为null,CLR对可空值实例中的值进行装箱 。
Int32? n = null; object o = n;
拆箱:可以将一个已装箱的值类型T拆箱成一个T或者一个Nullable<T>, 如果已装箱的值类型引用为null,就拆箱成一个Nullable<T>,
1 object obj1 = null; 2 object obj2 = 1; 3 int? o1 = (int?)obj1; 4 int? o2 = (int?)obj2; 5 int i1 = (int)obj1;//NullException 6 int i2 = (int)obj2;
Nullable<T>的类型
下面我们来看一下Nullable<T>的类型,可以使用代码查看,
1 2 Nullable<Int32> count = 3; 3 Console.WriteLine(count.GetType());//"System.Int32"
为什么Nullable<Int32>的类型和Int32的类型相同呢?如果他们真的相同,为什么不能直接赋值,要进行强制转换呢?这就是CLR对我们撒谎了。
??运算符
最后我们了解一下运算符“??”(null-coalescing operator 空接合运算符),他是一个类似于?:运算符的的运算符,a??b 表示如果a 不等于null,则返回a, 否则返回b。对于使用不使用,什么时候使用 仁者见仁智者见智,所以不多说,只给出一个例子。
1 Int32? temp = null; 2 Int32 temp1 = temp ?? 123; //Equals temp1 = temp.HasValue ? temp.Value:123;