LINQ, collections and null reference exceptions

This short post describes an idiosyncrasy of LINQ that, for someone with several years of C# experience I should probably have known about, but didn't.

During development of an app the test team reported that a null reference exception was intermittently occurring. For a seasoned developer like myself I didn't think it would be too hard to repro the issue and in fact null reference exception candidates can often be identified simply by eyeballing the code. I had a stack trace and so knew which method to look in.

From the method in question, I thought I saw the culprit and the story went like this.

Say you have a class of some type:

class Foo

{

public string FooString { get; set; }

}

 

And you use an instance of the type in a LINQ query:

Foo someFoo = new Foo();

List<Foo> fooCollection = new List<Foo>();

var matchingFoo = fooCollection.FirstOrDefault(f => f.FooString == someFoo.FooString);

 

All is well with the world. Now, what would happen when the following piece of code executes?

Foo someFoo = null;

List<Foo> fooCollection = new List<Foo>();

var matchingFoo = fooCollection.FirstOrDefault(f => f.FooString == someFoo.FooString);

 

someFoo is null and so when the predicate is evaluated, a null reference exception occurs. Problem solved … right?

Wrong! This code does not cause an error. However, the following code does:

Foo someFoo = null;

List<Foo> fooCollection = new List<Foo>();

fooCollection.Add(new Foo());

var matchingFoo = fooCollection.FirstOrDefault(f => f.FooString == someFoo.FooString);

 

The only difference is that in the first instance the collection is empty, in the second example it is populated.

Retrospectively, the answer is obvious. Under the covers the predicate would be expanded into a foreach loop which, for an empty collection, would never get executed.

Conclusion

This post is mostly for interest sake and the conclusions to be drawn are not revelatory. Always check for nulls and don't assume you know it all.

Written by Bradley Cotier