Join Null Check with Assignment

2017-07-16-join-null-check-with-assignment

I recently wrote some code and asked ReSharper to add a null check for me, which it did. Then it suggested that I could simplify the null check by joining it to the assignment.

Intrigued, I let it.

The code went from this:

public void SetMessage(string message)
{
    if (message == null) throw new ArgumentNullException(nameof(message));
    Message = message;
}

To this:

public void SetMessage(string message)
{
    Message = message ?? throw new ArgumentNullException(nameof(message));
}

So, I assign message to the property Message unless it is null in which case I throw the exception. This is a new feature in C# 7 called a “throw expression”.

At first glance, I thought it would still assign null to Message before throwing the exception, but that’s not what the code looks like underneath.

I got out my trusty dotPeek to see what it actually compiled to. (Don’t worry, I’m not going to show you IL, just what the C# looks like without the syntactic sugar). The result was this:

public void SetMessage(string message)
{
  string str = message;
  if (str == null)
    throw new ArgumentNullException("message");
  this.Message = str;
}

Excellent, it is still doing the null check in advance. So the semantics of what I wrote have not changed. That’s great. I learned something new today.

But…

ReShaper also suggested it in an overloaded version of that function that takes two parameters. And the result was not semantically equivalent. So, be careful. Here’s what happened there. I started with this:

public void SetMessage(string message, string transitionMessage)
{
    if (message == null) throw new ArgumentNullException(nameof(message));
    if (transitionMessage == null) throw new ArgumentNullException(nameof(transitionMessage));

    Message = message;
    TransitionMessage = transitionMessage;
}

Let ReSharper refactor to this:

public void SetMessage(string message, string transitionMessage)
{
    Message = message ?? throw new ArgumentNullException(nameof(message));
    TransitionMessage = transitionMessage ?? throw new ArgumentNullException(nameof(transitionMessage));
}

And, I’m beginning to get a little apprehensive at this point because I think I see a problem. In fact, when I look at it in dotPeek, I can see exactly what the issue is. Here’s the same code with the syntactic sugar removed:

public void SetMessage(string message, string transitionMessage)
{
  string str1 = message;
  if (str1 == null)
    throw new ArgumentNullException("message");
  this.Message = str1;
  string str2 = transitionMessage;
  if (str2 == null)
    throw new ArgumentNullException("transitionMessage");
  this.TransitionMessage = str2;
}

It does the first null check, then assigns to the Message property. Then it does the second null check… And that’s not what I want at all. This method should be an all or nothing proposition. Either both properties are set, or neither are changed and this isn’t the case any more.

Caveat Programmator, as they say in Latin.

LINQ: FirstOrDefault without the null check afterwards.

So, I was considering a problem I had that called for a LINQ statement that contains a FirstOrDefault() call. If there was an object returned I wanted a string property from it. If not, then I wanted to use a string.Empty.

I’ve often just caputured the result of the LINQ statement, checked for null, and if I had an object I’d call the property.

e.g.

var number = numbers.FirstOrDefault();
string firstNumberCaption = string.Empty;
if (number != null)
    firstNumberCaption = number.Caption;
Console.WriteLine("The first number is {0}", firstNumberCaption);

However, that code is a bit convoluted, and it occurred to me that there would be a better way of doing this.

The pure LINQ way

What if I called Select before the FirstOrDefault() to get the value of the property that I wanted. That way I don’t have to worry about implementing my check.

var firstNumber = numbers
    .Select(n => n.Caption)
    .FirstOrDefault();

And if I am desperate for the resulting string to be the empty string I can always append ?? string.Empty onto the end, like this:

var firstNumber = numbers
    .Select(n => n.Caption)
    .FirstOrDefault() ?? string.Empty;

If you are not familiar with the ?? (null-coalescing) operator you can find out more on MSDN.

Optimisation

My next concern was that perhaps it will convert every instance in the enumerable that was passed to LINQ before discarding them all but the first one. However, that doesn’t happen. For example, the following code only outputs one thing:

var firstNumber = numbers
    .Select(n =>
    {
        Console.WriteLine("Select {0} / {1}", n.Value, n.Caption);
        return n.Caption;
    })
    .FirstOrDefault() ?? string.Empty;

Filtering

I often put filters in my calls to FirstOrDefault(), and most of the time it is a filter on something other than what I’m returning in the Select part of the statement. Obviously, if we are performing the Select first, then the filter is not going to work if it is looking for data that is no longer there. In this case we just insert a Where statement just before the select to ensure that the filtering does happen.

So, for example, the first number in the sequence greater than 2:

var firstNumberCaption = numbers
    .Where(n => n.Value > 2)
    .Select(n => n.Caption)
    .FirstOrDefault() ?? string.Empty;

Console.WriteLine("The first number is {0}", firstNumberCaption);

Full program

Here is the full program (using the last example) if you want to see everything that is going on.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    class Number
    {
        public Number(int value, string caption)
        {
            Value = value;
            Caption = caption;
        }

        public int Value { get; set; }
        public string Caption { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Number[] numbers =
                {
                    new Number(1, "One"),
                    new Number(2, "Two"),
                    new Number(3, "Three")
                };

            var firstNumberCaption = numbers
                .Where(n => n.Value > 2)
                .Select(n => n.Caption)
                .FirstOrDefault() ?? string.Empty;

            Console.WriteLine("The first number is {0}", firstNumberCaption);

            Console.ReadLine();
        }
    }
}