空条件运算符评估为 bool 不为 bool?不出所料
Null-conditional operator evaluates to bool not to bool? as expected
我刚从 VS 2010 升级到 2015。我喜欢新的 null-conditional operator,它也被称为 null-propagation。这可以简化您的代码,例如:
string firstCustomerName = customers?[0].Name; // null if customers or the first customer is null
另一个:
int? count = customers?[0]?.Orders?.Count(); // null if customers, the first customer, or Orders is null
which returns a Nullable<int>
even if Enumerable.Count
returns a int
to distinctive between a valid count and any nulls
before .这非常直观且非常有用。
但为什么它会按预期编译和工作(returns false
):
string text = null;
bool contains = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase) >= 0;
它应该 return bool?
(它没有)或者不编译。
你实际拥有的是
string text = null;
int? index = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = index >= 0;
和int? >= int
是完全合法的。
它被拆分的原因是 the documentation for the operator 指出 "If one operation in a chain of conditional member access and index operation returns null, then the rest of the chain’s execution stops. Other operations with lower precedence in the expression continue." 这意味着 .?
只会评估具有相同优先级或更高优先级的事物在它之前 "creates a value".
如果您查看 the order of operator precedence,您会发现 "Relational and Type-testing Operators" 在列表中的位置要低得多,因此将在应用 >=
之前创建该值。
更新: 因为它是在评论中提到的,这里是 C# 5 规范部分关于 >=
和其他运算符在处理可为空时的行为方式价值。我找不到 C# 6 的文档。
7.3.7 Lifted operators
Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable
forms of those types. Lifted operators are constructed from predefined
and user-defined operators that meet certain requirements, as
described in the following:
For the unary operators
+ ++ - -- ! ~
a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is
constructed by adding a single ? modifier to the operand and result
types. The lifted operator produces a null value if the operand is
null. Otherwise, the lifted operator unwraps the operand, applies the
underlying operator, and wraps the result.
For the binary operators
+ - * / % & | ^ << >>
a lifted form of an operator exists if the operand and result types
are all non-nullable value types. The lifted form is constructed by
adding a single ? modifier to each operand and result type. The lifted
operator produces a null value if one or both operands are null (an
exception being the & and | operators of the bool? type, as described
in §7.11.3). Otherwise, the lifted operator unwraps the operands,
applies the underlying operator, and wraps the result.
For the equality operators
== !=
a lifted form of an operator exists if the operand types are both
non-nullable value types and if the result type is bool. The lifted
form is constructed by adding a single ? modifier to each operand
type. The lifted operator considers two null values equal, and a null
value unequal to any non-null value. If both operands are non-null,
the lifted operator unwraps the operands and applies the underlying
operator to produce the bool result.
For the relational operators
< > <= >=
a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. The lifted
form is constructed by adding a single ? modifier to each operand
type. The lifted operator produces the value false if one or both
operands are null. Otherwise, the lifted operator unwraps the operands
and applies the underlying operator to produce the bool result.
如果您使用此代码,并将鼠标悬停在 x
上,您会看到 x
是 int?
:
var x = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = x >= 0;
所以打字还是正确的。
然后让我们看一下x >= 0
:那是一个int? >= int
。显然,可空结构和不可空结构之间有一个运算符。这就是它起作用的原因。
如果您查看 IL,您会发现它实际上调用了 HasValue
和 GetValueOrDefault()
。我猜是有一个操作符在做这个,但是我在参考源中找不到它,所以它应该在 CLR 或编译器中:
instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
...
instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
我刚从 VS 2010 升级到 2015。我喜欢新的 null-conditional operator,它也被称为 null-propagation。这可以简化您的代码,例如:
string firstCustomerName = customers?[0].Name; // null if customers or the first customer is null
另一个:
int? count = customers?[0]?.Orders?.Count(); // null if customers, the first customer, or Orders is null
which returns a Nullable<int>
even if Enumerable.Count
returns a int
to distinctive between a valid count and any nulls
before .这非常直观且非常有用。
但为什么它会按预期编译和工作(returns false
):
string text = null;
bool contains = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase) >= 0;
它应该 return bool?
(它没有)或者不编译。
你实际拥有的是
string text = null;
int? index = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = index >= 0;
和int? >= int
是完全合法的。
它被拆分的原因是 the documentation for the operator 指出 "If one operation in a chain of conditional member access and index operation returns null, then the rest of the chain’s execution stops. Other operations with lower precedence in the expression continue." 这意味着 .?
只会评估具有相同优先级或更高优先级的事物在它之前 "creates a value".
如果您查看 the order of operator precedence,您会发现 "Relational and Type-testing Operators" 在列表中的位置要低得多,因此将在应用 >=
之前创建该值。
更新: 因为它是在评论中提到的,这里是 C# 5 规范部分关于 >=
和其他运算符在处理可为空时的行为方式价值。我找不到 C# 6 的文档。
7.3.7 Lifted operators
Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:
For the unary operators
+ ++ - -- ! ~
a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single ? modifier to the operand and result types. The lifted operator produces a null value if the operand is null. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result.
For the binary operators
+ - * / % & | ^ << >>
a lifted form of an operator exists if the operand and result types are all non-nullable value types. The lifted form is constructed by adding a single ? modifier to each operand and result type. The lifted operator produces a null value if one or both operands are null (an exception being the & and | operators of the bool? type, as described in §7.11.3). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result.
For the equality operators
== !=
a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. The lifted form is constructed by adding a single ? modifier to each operand type. The lifted operator considers two null values equal, and a null value unequal to any non-null value. If both operands are non-null, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.
For the relational operators
< > <= >=
a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. The lifted form is constructed by adding a single ? modifier to each operand type. The lifted operator produces the value false if one or both operands are null. Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.
如果您使用此代码,并将鼠标悬停在 x
上,您会看到 x
是 int?
:
var x = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = x >= 0;
所以打字还是正确的。
然后让我们看一下x >= 0
:那是一个int? >= int
。显然,可空结构和不可空结构之间有一个运算符。这就是它起作用的原因。
如果您查看 IL,您会发现它实际上调用了 HasValue
和 GetValueOrDefault()
。我猜是有一个操作符在做这个,但是我在参考源中找不到它,所以它应该在 CLR 或编译器中:
instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
...
instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()