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:
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.
If you use this code, and hover over x
, you see that x
is a int?
:
var x = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = x >= 0;
So the typing is still correct.
Then lets look into x >= 0
: that is a int? >= int
. Appearantly there is an operator between the nullable and non-nullable structs. That is why it works.
If you look at the IL, you will see it actually calls HasValue
and GetValueOrDefault()
. I guess there is an operator doing this, but I couldn't find it in the reference source, so it should be in the CLR or compiler:
instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
...
instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()