20

I've been playing with C# 6's Null Conditional Operator (more info here).

I really like the syntax and I think it makes the code much more readable however I think it is questionable as to what exactly the code is going to do when you come across checking the value of a property on an object which itself might be null.

For example, if I had a class with a decimal property on it and I wanted a conditional check on the value of that decimal, I would write something like:

if (foo?.Bar > max)
{
   // do something
}

On the surface this looks great... If foo is not null, get the value of Bar and check if it's greater than a maximum value, if it is, do something.

However, what if foo is null?!

This documentation about the new and improved features of C# 6 says something along these lines:

if in fact the value of the object is null, the null-conditional operator will return null. It short-circuits the call to Bar, and immediately returns null, avoiding the programming error that would otherwise result in a NullReferenceException.

I've written a fiddle here which shows that it does indeed work and is doing what I'm expecting it to do however I can't get my head around how it is deciding the result of the condition.

How does the short-circuit equal a false? In my head this code is now going to say "If foo is null, check if null is > max, which is impossible, so return false" or "If foo is null, then foo != null will return false, so you get a false" however the documentation says the null conditional check returns null, not false.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Danny Lager
  • 371
  • 1
  • 4
  • 17
  • Interestingly, it will not run `do something` whether the condition is `(bar > max)` or `(bar <= max)`. So basically, a `null` value in a condition will always evaluate to `false`, no matter what the condition actually is. – jcb Aug 21 '17 at 12:11
  • 1
    You should use `(foo?.Bar).GetValueOrDefault(2)` to set a value to use for comparison if foo is null. I specified "2" but if you leave out the parameter it defaults to zero. – Grax32 Aug 21 '17 at 12:23

3 Answers3

17

How does the short-circuit equal a false?

if (foo?.Bar > max)
{
   // do something
}

is roughly equivalent to:

Decimal? bar = null;
if (foo != null)
    bar = foo.Bar;

if (bar > max)
{
   // do something
}

Thus, the short circuit doesn't equal false. It equals a Decimal? (nullable Decimal) which is then compared to max.

See also: How does comparison operator works with null int?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
mjwills
  • 23,389
  • 6
  • 40
  • 63
  • This makes sense. The documentation says it returns null but this would mean it is clever enough to know that the null returned is actually a nullable with a value of null... has anyone reflected what the ?. is actually doing internally? – Danny Lager Aug 21 '17 at 13:22
  • 2
    Click on the links in my post @DannyLager . One of them shows how Roslyn lowers it. It basically converts the `?.` to something like what I have above (not exactly the same, but close enough). – mjwills Aug 21 '17 at 13:23
  • 2
    `The documentation says it returns null` Fundamentally, it needs to return a `null` of a specific **Type**. And given the `Bar` property was a `Decimal`, `Decimal?` is the obvious choice. – mjwills Aug 21 '17 at 13:27
  • Thanks I had looked at the first link but not understood what it was! Second link is very interesting, I didn't know that all operators (except !=) would handle a null in this way although I've never come across an issue with this in the past! – Danny Lager Aug 21 '17 at 13:27
  • Your link to 'roughly equivalent' indicates which line of code in source as I am not able to find relevant code. – Akash KC Aug 23 '17 at 18:32
  • 1
    @AkashKC It is a link to Roslyn source code that lowers `?.`. As to what lowering is, see https://stackoverflow.com/a/45772116/34092 . You may enjoy reading https://msdn.microsoft.com/en-us/magazine/dn802602.aspx?f=255&MSPPError=-2147217396 as well. – mjwills Aug 23 '17 at 23:14
  • +1. I had never heard about 'lowering' term before. Time to investigate it more and thanks for useful resource linked in your comment and answer – Akash KC Aug 24 '17 at 00:48
7

It short-circuits the call to Bar

means stop checking the following steps (.) within an object reference chain if the parent is already null. This means operators like a comparison are not affected because you are using the value instead of moving in the chain. This behaviour is called null propagation. You can find some nice descriptions at Code Project or Dave Fancher.

Using the null conditional operator returns a nullable value like double?. This value is then compared with max. The behaviour of a null in such a comparision is well described by Microsoft:

When you perform comparisons with nullable types, if the value of one of the nullable types is null and the other is not, all comparisons evaluate to false except for != (not equal).

This means:

if (null > max)
{
    //never called
}
Fruchtzwerg
  • 10,999
  • 12
  • 40
  • 49
-1

The Null Conditional Operator should be used only for scenarios regarding objects properties's multiple assignments (see objects mapping, etc..) where it could be boring verify every time if occurs the null condition of the property.

Or also for scenarios like the following :

int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null 

Using this operator into an if test expression it could lead to unexpected behaviours, for example in your if statements case on fiddler you get a "passed" test (the last if statement) but with a null value, which is obviously not a valid case

Ciro Corvino
  • 2,038
  • 5
  • 20
  • 33