C# 运算符的简化操作(五):可空类型和运算符 空合并运算符

09  可空类型和运算符

值类型和引用类型的一个重要区别是,引用类型可以为空。值类型(如 int)不能为空。把 C#类型映射到数据库类型时,这是一个特殊的问题。数据库中的数值可以为空。在早期的 C#版本中,一个解决方案是使用引用类型来映射可空的数据库数值。然而,这种方法会影响性能,因为垃圾收集器需要处理引用类型。现在可以使用可空的 int 来替代正常的 int。其开销只是使用一个额外的布尔值来检查或设置空值。可空类型仍然是值类型。.

在下面的代码片段中,变量 i1 是一个 int,并给它分配 1。i2 是一个可空的 int,给它分配 il。可空性使用?和类型来定义。给 int?分配整数值的方式类似于 i1 的分配。变量 i3 表明,也可以给可空类型分配 null:

int i1 = 1;

int? i2 = 2;

int? i3 = null;

每个结构都可以定义为可空类型,如下面的 long?和 DateTime?所示:

long? 11 = null;

DateTime? d1 = null;

如果在程序中使用可空类型,就必须考虑 null 值在与各种运算符一起使用时的影响。通常可空类型与一元或二元运算符一起使用时,如果其中一个操作数或两个操作数都是 null,其结果就是 null。例如:

int? a = null;

int? b = a + 4;  // b = null 

int? c = a * 5;    // c = null

但是在比较可空类型时,只要有一个操作数是 null,比较的结果就是 false。即不能因为一个条件是 false,就认为该条件的对立面是 true,这种情况在使用非可空类型的程序中很常见。例如,在下面的例子中,如果 a 是空,则无论 b 的值是 +5 还是 -5,总是会调用 else 子句:

int? a = null; int? b = -5;if (a >= b) // if a or b is null, this condition is false{  Console.WriteLine("a >= b");}else{  Console.WriteLine("a < b");}

注意

null 值的可能性表示,不能随意合并表达式中的可空类型和非可空类型。

 注意

使用 C#关键字?和类型声明时,例如 int?,编译器会解析它,以使用泛型类型Nullable。C#编译器把速记符号转换为泛型类型,来减少输入量。

10  空合并运算符

空合并运算符(??)提供了一种快捷方式,可以在处理可空类型和引用类型时表示 null 值的可能性。这个运算符放在两个操作数之间,第一个操作数必须是一个可空类型或引用类型:第二个操作数必须与第一个操作数的类型相同,或者可以隐式地转换为第一个操作数的类型。空合并运算符的计算如下:

· 如果第一个操作数不是 null,整个表达式就等于第一个操作数的值。

·  如果第一个操作数是 null,整个表达式就等于第二个操作数的值。

例如:

int? a = null;int b;b = a ?? 10; // b has the value 10a = 3;b = a ?? 10; // b has the value 3

如果第二个操作数不能隐式地转换为第一个操作数的类型,就生成一个编译时错误。

空合并运算符不仅对可空类型很重要,对引用类型也很重要。在下面的代码片段中,属性 Val 只有在不为空时才返回_val 变量的值。如果它为空,就创建 MyClass 的一个新实例,分配给_val 变量,最后从属性中返回。只有在变量_val为空时,才执行 get 访问器中表达式的第二部分。

private MyClass _val; public MyClass Val{  get => _val ?? (_val = new MyClass());