前序
在某些情况下,特别是使用数据库的时候,你希望表示变量目前未保存有效的值。对于引用类型,这很简单,可以把变量设置为null。但定义值类型的变量时,不管它的内容是否有有效的意义,其内存都会进行分配。
对于这种情况,你可能会使用一个布尔指示器来和变量关联,如果值有效,则设置为true,否则就设置为false。
可空类型允许创建可以标记为有效或无效的值类型,这样就可以在使用它之前确定值的有效性
概念
可空类型允许我们创建一个值类型变量并且可以标记为有效或无效,这样我们就可以有效地把值类型设置为“null”。
可空类型总是基于另外一个叫做基础类型的已经被声明的类型。
注意
- 可以从任何值类型创建可空类型,包括预定义的简单类型。
- 不能从引用类型或其他可空类型创建可空类型。
- 不能在代码中显式声明可空类型,只能声明可空类型的变量。编译器会使用泛型隐式地创建可空类型。
要创建可空类型的变量,只需要在变量声明中的基础类型的名字后面加一个问号。
语法
int? myNInt = 28; //可空类型的名字包含后缀 ?
HasValue
属性是bool类型,并且指示值是否有效;Value
属性是和基础类型相同的类型并且返回变量的值——如果变量有效的话。
使用可空类型基本与使用其他类型的变量一样。读取可空类型的变量返回其值。但是你必须确保变量不是null的,尝试读取一个null的变量会产生异常。
- 跟任何变量一样,要获取可空类型变量的值,使用名字即可。
- 要检测可空类型是否具有值,可以将它和null比较或者检查它的
HasValue
属性。
非可空类型和相应的可空版本之间的转换是隐式的,也就是说,不需要强制转换;
可空类型和相应的可空版本之间的转换是显式的。
使用空接合运算符
允许我们在可空类型变量为null时返回一个值给表达式。
空接合运算符由两个连续的问号组成,它有两个操作数。
- 第一个操作数是可空类型的变量。
- 第二个是相同基础类型的不可空值。
- 在运行时,如果第一个操作数运算后为null,那么第二个操作数就会被返回作为运算结果。
例
int? myI4 = null;
Console.WriteLine("myI4:{0}", myI4 ?? -1);
myI4 = 10;
Console.WriteLine("myI4:{0}", myI4 ?? -1);
结果
myI4:-1
myI4:10
如果比较两个相同可空类型的值,并且都设置为null,那么相等比较运算符会认为它们是相等的(==和!=)。
例如,在下面的代码中,两个可空的int被设置为null,相等比较运算符会声明它们是相等的。
int? i1 = null, i2 = null;
if(i1 == i2) {
Console.WriteLine("相等");
}
结果
相等
使用可空用户自定义类型
一个可空类型不直接暴露基础类型的任何成员
例
代码声明了一个叫做MyStruct的结构(值类型),它有两个公共字段。
- 由于结构的字段是公共的,所以它可以被结构的任何实例所访问到,如图左部分所示。
- 然而,结构的可空形式只通过
value
属性暴露基础类型,它不直接暴露它的任何成员。尽管这些成员对结构来说是公共的,但是它们对可空类型来说不是公共的,如图右部分所示。
struct MyStruct //声明结构
{
public int X;
public int Y;
public MyStruct(int xVal, int yVal) { //构造函数
X = xVal;
Y = yVal;
}
}
class Program
{
static void Main() {
MyStruct mSStruct = new MyStruct(6, 11); //结构变量
MyStruct? mSNull = new MyStruct(5, 10); //可空类型变量
Console.WriteLine("mSStruct.X:{0}", mSStruct.X);
Console.WriteLine("mSStruct.Y:{0}", mSStruct.Y);
Console.WriteLine("mSNull.X:{0}", mSNull.Value.X);
Console.WriteLine("mSNull.Y:{0}", mSNull.Value.Y);
}
}
可空类型通过一个叫做System.Nullable<T>
的.NET类型来实现,它使用了C#的泛型特性。C#可空类型的问号语法是创建Nullable<T>
类型变量的快捷语法,在这里T就是基础类型。Nullable<T>
接受了基础类型并把它嵌入结构中,同时给结构提供可空类型的属性、方法和构造函数。
我们可以使用Nullable<T>
这种泛型语法,也可以使用C#的快捷语法。快捷语法更容易书写和理解,并且也减少了出错的可能性。以下代码使用Nullable<T>
语法为之前示例中声明的MyStruct结构创建一个叫做mSNul1的Nullable<MyStruct>
类型:
Nullable<MyStruct> mSNull = new Nullable<MyStruct>();
//等同于
MyStruct? mSNull = new MyStruct();