Mike Alain LaPatrice
Mike Alain LaPatrice

Reputation: 39

When using a null conditional operator in a conditional statement, how is the null case handled?

Say I have the following code here:

static void Main(string[] args)
{
   StringBuilder[] s = new StringBuilder[3];
   if (s[0]?.Length > 0)
   {
       Console.WriteLine("hi");
   }
}

My understanding is that the expression that goes inside the if statement, must be a boolean expression. A boolean expression (my understanding I could be wrong) is an expression that evaluates to true or false.

In this case, the null-conditional operator will return null since, the default value of an element within an array of reference-type variables, is null. Therefore, this if statement is equivalent to

bool? x = null;
if (x)
{ 
   // do cool things here */ 
}

But this gives me a syntax error: cannot convert null to bool.

Therefore, how it is possible that the above example with StringBuilder works? My understanding that the better approach to the code above should be to combine it with null-coalescing operator, as in:

if (s[0]?.Length > 0 ?? false) {}

Thanks all :)

Upvotes: 2

Views: 346

Answers (3)

TheGeneral
TheGeneral

Reputation: 81583

In your statement

if (x)

x not a Boolean Expression it's a Nullable bool. In short, you can't put statements that do not evaluate to a Boolean expression in an if statement.

However, you can Lift it by specifying true or false, this takes advantage of the Nullable Lifted operators. E.g

if (x == true)
//or
if (x == false)

Similarly, lifting is what's causing the following to work.

int? i = null;
if(i > 0) {...}

This can also be seen with the Null-Conditional operator

if (SomeObj?.Length > 0) 

What's happening here is, the Null-Conditional is forcing Length (which is an int) to be interpreted as a Nullable int it gets checked for null and lifted to int for the comparison

Note : The actual generated IL when reinterpreted would look something like this

if ((object)obj == null || obj.Length <= 0)

Even though the generated code is counter-intuitive, lifting is a special case for Nullable-Types and the compiler treats them differently for various conversion and operators

You can find out more by perusing the Standard ECMA-334 5th Edition specifications on Lifted operators and Lift Conversions

12.4.8 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

Upvotes: 5

Enigmativity
Enigmativity

Reputation: 117175

Your equivalence is wrong.

Writing var x = s[0]?.Length > 0; is the same as bool x = false, not bool? x = null;

The un-optimized IL produced is:

IL_0013:  ldloc.0     // s
IL_0014:  ldc.i4.0    
IL_0015:  ldelem.ref  
IL_0016:  dup         
IL_0017:  brtrue.s    IL_001D
IL_0019:  pop         
IL_001A:  ldc.i4.0    
IL_001B:  br.s        IL_0025
IL_001D:  call        System.Text.StringBuilder.get_Length
IL_0022:  ldc.i4.0    
IL_0023:  cgt         
IL_0025:  stloc.1     // x

The line brtrue.s simply branches when s[0] is "true, not null, or non-zero" and thus jumps to the code that compares the .Length with 0 - otherwise it hits br.s branch which simply jumps to the code that sets the result to false.

Hence it is the equivalent of bool x = false and not bool? x = null

My understanding is that the expression that goes inside the if statement, must be a boolean expression. A boolean expression (my understanding I could be wrong) is an expression that evaluates to true or false.

You are correct in your understanding.

The code de-compiles in ILSpy as:

StringBuilder[] array = new StringBuilder[3];
StringBuilder obj = array[0];
if (obj != null && obj.Length > 0)
{
    Console.WriteLine("hi");
}

Upvotes: 1

Mike Alain LaPatrice
Mike Alain LaPatrice

Reputation: 39

Sigh, I believe a bit more Googling has afforded me an answer! This question here helped me out: Trying to understand ?. (null-conditional) operator in C#

Basically, for certain operators (I believe <, >, <=, >=) these operators are 'lifted' to deal with the null case. If either of the operands in these binary operators are null, they automatically return false.

For example

int? x = 5;
int? y = 10;
bool b = x < y; // true

Why does this work? Because it's semantically equivalent to:

bool b = (x.HasValue && y.HasValue) ? (x.Value < y.Value) : false;

If one of the operands doesn't have a value, always return false.

Thanks SO!

Upvotes: -1

Related Questions