Reputation: 39
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
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
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
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