Go back

C# coalesce operator (double question mark) gotcha

I'm sure you've met the C# coalesce operator, where you can use a double question mark to check for null:

x = y ?? z;

... being equivalent to:

<span style="color: #0000ff">if</span> (y != <span style="color: #0000ff">null</span>)
    x = y;
<span style="color: #0000ff">else</span>
    x = z;

It's a handy shortcut, but sometimes it doesn't behave quite as you I might expect.

Here's an example:

I wanted to combine the elements of an address to make a field that was searchable with a SQL LIKE statement. As we use LINQ to SQL, I needed an expression that LINQ to SQL could translate into SQL syntax. I wrote this:

...
select <span style="color: #0000ff">new</span>
{
    SearchableAddress = c.Address.HouseName   ?? &quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot;
                      + c.Address.HouseNumber ?? &quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot;
                      + c.Address.Road        ?? &quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot;
                      + c.Address.Town        ?? &quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot;
                      + c.Address.County      ?? &quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot;
                      + c.Address.Postcode    ?? &quot;<span style="color: #8b0000"></span>&quot;
}

I'm using the coalesce operator to replace nulls with empty strings - otherwise SQL returns NULL for the whole expression if a single element is NULL.

However, I found my search was rarely getting any matches, and a little digging isolated the above expression as the culprit.

It turns out that the coalesce operator takes lower precedence than the concatenation operator. What I meant (and what I should have written) was this:

...
select <span style="color: #0000ff">new</span>
{
    SearchableAddress = (c.Address.HouseName   ?? &quot;<span style="color: #8b0000"></span>&quot;) + &quot;<span style="color: #8b0000"> </span>&quot;
                      + (c.Address.HouseNumber ?? &quot;<span style="color: #8b0000"></span>&quot;) + &quot;<span style="color: #8b0000"> </span>&quot;
                      + (c.Address.Road        ?? &quot;<span style="color: #8b0000"></span>&quot;) + &quot;<span style="color: #8b0000"> </span>&quot;
                      + (c.Address.Town        ?? &quot;<span style="color: #8b0000"></span>&quot;) + &quot;<span style="color: #8b0000"> </span>&quot;
                      + (c.Address.County      ?? &quot;<span style="color: #8b0000"></span>&quot;) + &quot;<span style="color: #8b0000"> </span>&quot;
                      + (c.Address.Postcode    ?? &quot;<span style="color: #8b0000"></span>&quot;)
}

... but my version without any parentheses was equivalent to this:

...
select <span style="color: #0000ff">new</span>
{
    SearchableAddress =             c.Address.HouseName    ?? 
                        (&quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot; + c.Address.HouseNumber) ?? 
                        (&quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot; + c.Address.Road)        ?? 
                        (&quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot; + c.Address.Town)        ?? 
                        (&quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot; + c.Address.County)      ?? 
                        (&quot;<span style="color: #8b0000"></span>&quot; + &quot;<span style="color: #8b0000"> </span>&quot; + c.Address.Postcode)    ??
                         &quot;<span style="color: #8b0000"></span>&quot;
}

Getting back the first non-null element, padded with a superfluous space, was enough to make the search work occasionally enough to be confusing.

Maybe I should have seen it coming, as I'd happily use:

a = b ?? c ?? d ?? e;

to get the first non-null value, but that string concatenation threw me off the scent.

I dunno - to me coalesce feels like a unary operator, so I'd expect it to be evaluated first; it turns out it gets evaluated almost last. What do you think?

Got a project? Let's work together

Compsoft is a remote working company except for Thursdays when we work together from the Alton Maltings in Alton, Hampshire (UK). Our registered address is detailed below. We'd be delighted to have either a virtual chat with you or to make arrangements to meet in person.