Using is instead of == to check whether an object is a null reference

A tweet yesterday by Bertrand Le Roy:

Left me slightly wanting, I didn't disagree that it's prettier, but I wanted to know why it was more robust. It turns out the answer wasn't too hard to find, by drilling down into the comments on the recent Welcome to C# 7.1 post on the .NET Blog (emphasis mine):

I agree with your general sentiment. In this case, the meaning of “is 0” falls out as a corner case of a bigger feature, pattern matching, that actually DOES add significant new expressiveness. I agree that I probably shouldn’t have used it here though, and wouldn’t in my own code. I couldn’t resist putting it in there since we were focused on new features.

A place where I WILL start using “is” instead of “==” is for null checking. That’s because they don’t actually mean exactly the same thing. In “s == null”, the “==” operator can be overloaded, and all of a sudden you don’t get the reference equality you thought you did. Use of a constant pattern, as in “s is null”, calls Object.Equals, which will always check for null by reference. So it’s a more reliable, less fragile null check than “==”.

By taking advantage of the pattern matching goodness introduced in C# 7.0, specifically the is expression, you can make your code more robust in the face of crazy. As Mads Torgersen outlines in the comment, doing this bypasses any code in an overloaded == operator and calls down into Object.Equals so you get a reference equality check. Imagine, for a moment, this class:

class DontDoThis
{
    public static bool operator ==(DontDoThis item1, DontDoThis item2)
    {
        // This is just bad code (to make a point!, don't do this)
        if ((object)(item1 ?? item2) == null)
        {
            throw new ArgumentNullException();
        }
        else
        {
            return false;
        }
    }

    public static bool operator !=(DontDoThis item1, DontDoThis item2) => !(item1 == item2);
}

Just pretend that whoever wrote the code thought they had a good reason for blowing up if both the instances of DontDoThis being checked were null (and return false in every other case!!), and try this in a basic console application (built/running against .NET Core 2, just because):

class Program
{
    static void Main(string[] args)
    {
        DontDoThis item = null;

        if (item == null)
        {
            Console.WriteLine("I'm never going to get here!");
        }
    }
}

Seeing your code blow up here is utterly unintuitive, but luckily C# 7.0 comes to the rescue and this version of the code:

class Program
{
    static void Main(string[] args)
    {
        DontDoThis item = null;

        if (item is null)
        {
            Console.WriteLine("I am going to get here!");
        }
    }
}

Doesn't blow up, and instead writes I am going to get here! to the console as you'd expect - from either version of the code! One side note, that's interesting as a curio, is that this gives a syntax and behaviour that's consistently closer to that of SQL for checking whether something is NULL or not, take the following code as an example:

DECLARE @int INT

IF (@int = NULL)
BEGIN
	PRINT 'Equals returns TRUE'
END
ELSE
BEGIN
	PRINT 'Equals returns FALSE'
END

IF (@int IS NULL)
BEGIN
	PRINT 'IS returns TRUE'
END
ELSE
BEGIN
	PRINT 'IS returns FALSE'
END

Gives the output:

Equals returns FALSE
IS returns TRUE

I know, it's not the same, but it feels nice that the same form of words gives you correct NULL checking in C# and SQL, as I've seen many junior developers make the = NULL mistake in SQL before.

About Rob

I've been interested in computing since the day my Dad purchased his first business PC (an Amstrad PC 1640 for anyone interested) which introduced me to MS-DOS batch programming and BASIC.

My skillset has matured somewhat since then, which you'll probably see from the posts here. You can read a bit more about me on the about page of the site, or check out some of the other posts on my areas of interest.

No Comments

Add a Comment