出于对数据库访问的需要,C#中定义了一种类型,称为nullable类型。比如,在访问数据库时,需要返回某个整型字段的值。但是,当数据库中不存在符合条件的纪录时,返回的将是null。这样,以下这段代码便是非法:
int result = aDbCmd.ExecuteScalar(); // aDbCmd.ExecuteScalar() returns null to indicate no result fetched.
MS提到了普遍使用的三种解决方法:
1、用一个特殊值来表示。缺点在于你需要确定确实有一个值,除了用来指示是否为空外永远不会被用到;
2、使用一个bool型的字段或者变量来指示。缺点在于难于作为参数和返回值使用;
3、定义一个自定义的可空类型。但只能用于一个封闭的,无法拓展的类型集合。
于是MS使用了nullable类型。其实在我看来,MS只不过是利用了它对C#的控制,扩充了第三种解决方法,将某种东西附加到每个non-nullable的类型上。在C#中,nullable的表示是在类型之后加上“?”。可以认为以下两种定义是等价的:
T?等价于
struct nullable
{
private T value;
private bool nullIndicator;
public T Value
{
get
{
if (!nullIndicator)
throw new InvalidOperationException();
return value;
}
}
public bool HasValue
{
get { return nullIndicator; }
}
public nullable(T aValue)
{
value = aValue;
nullIndicator = true;
}
public nullable()
{
value = UnInitializedT;
nullIndicator = false;
}
// ...
}
也就是说,T? anObj与nullable anObj是等价的。
对于nullable对象的使用,如下:
int? x = 123;
int? y = null;
if (x.HasValue) Console.WriteLine(x.Value);
if (y.HasValue) Console.WriteLine(y.Value); // Nothing written.
好了,有了这些,我们就可以开始考察关于nullable的一些规则了。首先解释几个概念:
1、nullable转换和提升的转换。这两种类型转换允许预定义和自定义的转换同时能够用于non-nullable的值类型和这些类型的nullable形式。
2、提升的操作符。允许预定义和自定义的操作符同时能够用于non-nullable的值类型和这些类型的nullable形式。
3、null传播。直接将null转换成null,否则执行底层类型的non-nullable的转换。
概念也清楚了,本质也清楚了,那么就不难搞懂下面这些规则了:
1、底层的类型必须是non-nullable的值类型。啥也不用解释了,看看上面关于nullable的定义就明白。
2、隐含的类型转换。比如: int? a = 123;这里将一个字面常量隐式转换成nullable的int,其实这里就是创建一个nullableT,并用123去初始化它。
3、null传播。两个non-nullable的类型s和t,s?和t?之间的类型转换就是将底层的s和t进行转换,在将它们分别装到s?和t?中。从s到t?和从t到s?也一样。但是,将s?转换到t,或者t?转换到s是就会存在null传播问题:此时相当于将null赋值给non-nullable的值类型,.NET Framework将会抛出一个异常,如nullable中Value属性所示。即:
int? a = null;
int b = (int)a; // Exception will be thrown.
4、提升的类型转换。也就是说,本来用于non-nullable的转换操作被提升到nullable的转换操作,对应nullable的类型转换。
5、提升的操作符。首先说说非比较操作符。本来用于non-nullable的操作被提升到nullable的操作,对应nullable的操作。如:
int? a = 123;
int? b = 456;
int? x = a + b;
此处“+”操作符本来只能操作int,但是由于操作符提升,该操作被提升为两个nullable之间的加操作。根据null传播规则,若a和b中有任一个是null,那么x的结果就是null。
对于提升的比较操作符,其语义与底层类型的比较操作符一致。然而,两个nullable对象都是null,那么这两个对象是相等的。当提升的比较操作符的两个操作数其一或者两者为null,<,>,<=,>=返回的都是false。也就是说,提升的比较操作符上升到了比较nullable对象——无法判断某个值与null的大小。
说到底,将T?理解成nullable有助于理解上述规则。如果记不住上述的这些规则(以及一些此处未阐述的规则),记住这一条就行了。
PS. 笔者认为本文中介绍的技术不是很有用。
参考:
C# 2.0 / 3.0 Specification
Copyleft (C) 2007-2009 raof01.
本文可以用于除商业外的所有用途。此处“用途”包括(但不限于)拷贝/翻译(部分或全部),不包括根据本文描述来产生代码及思想。若用于非商业,请保留此
权利声明,并标明文章原始地址和作者信息;若要用于商业,请与作者联系(raof01@gmail.com),否则作者将使用法律来保证权利。
阅读(5369) | 评论(6) | 转发(0) |