Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Null-conditional operator evaluates to bool not to bool? as expected

Tags:

I've just upgraded from VS 2010 to 2015. I like the new null-conditional operator which is also known as null-propagation. This enables to simplify your code, for example:

string firstCustomerName = customers?[0].Name; // null if customers or the first customer is null 

another one:

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 an int to differentiate between a valid count and any nulls before. That's quite intuitive and very useful.

But why does this compile and work as expected (it returns false):

string text = null; bool contains = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase) >= 0; 

It should either return bool? (which it does not) or not compile.

like image 671
Tim Schmelter Avatar asked May 17 '16 13:05

Tim Schmelter


People also ask

When to use null forgiving operator?

When we are confident that an object will not be null, we can use the null forgiving operator (also called the null suppressing operator) to remove the warning. We do this by using an exclamation point. In the following example, the analyzer thinks that the "Exception" property on task may be null.

What is a nullable Boolean?

You typically use a nullable value type when you need to represent the undefined value of an underlying value type. For example, a Boolean, or bool , variable can only be either true or false . However, in some applications a variable value can be undefined or missing.

What is null conditional operator c#?

A null-conditional operator applies a member access, ?. , or element access, ?[] , operation to its operand only if that operand evaluates to non-null; otherwise, it returns null .


1 Answers

What you actually have is

string text = null; int? index = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase); bool contains = index >= 0; 

and int? >= int is perfectly legal.

The reason it was split there is the documentation for the operator states "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." That means .? will only evaluate things with the same precedence or higher before it "creates a value".

If you look at the order of operator precedence you will see that "Relational and Type-testing Operators" are much lower in the list so the value will be created before the >= is applied.


UPDATE: Because it was brought up in the comments, here is the C# 5 spec section on how the >= and other operators behave when dealing with a nullable value. I could not find a document for 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.

like image 53
Scott Chamberlain Avatar answered Nov 03 '22 00:11

Scott Chamberlain