DEV Community

Cover image for NullReferenceException & LINQ XOrDefault methods
Cesar Aguirre
Cesar Aguirre

Posted on • Edited on • Originally published at canro91.github.io

2 1 1

NullReferenceException & LINQ XOrDefault methods

I originally posted an extended version of this post on my blog."


Last time, we covered when NullReferenceException is thrown.

Here,

This time, let's learn how to prevent NullReferenceException when working with LINQ XOrDefault methods.

If we don't check the result of LINQ FirstOrDefault(), LastOrDefault(), and SingleOrDefault() methods, we could get NullReferenceException.

In general, the XOrDefault() methods return null when the source collection has reference types, and there are no matching elements.

Let's focus on FirstOrDefault(). It finds the first element of a collection or the first element matching a condition. If the collection is empty or doesn't have matching elements, it returns the default value of the collection's type. For reference types, that's a null.

For example, let’s find in our catalog of movies a “perfect” film,

var movies = new List<Movie>
{
    new Movie("Shrek", 2001, 3.95f),
    new Movie("Inside Out", 2015, 4.1f),
    new Movie("Ratatouille", 2007, 4f),
    new Movie("Toy Story", 1995, 4.1f),
    new Movie("Cloudy with a Chance of Meatballs", 2009, 3.75f)
};

// We don't have any perfect movie on our list,
// FirstOrDefault returns null
var theBest = movies.FirstOrDefault(movie => movie.Rating == 5.0);
Console.WriteLine(theBest.Name);
//                👆👆👆
// Boooom!

record Movie(string Name, int ReleaseYear, float Rating);
Enter fullscreen mode Exit fullscreen mode

We could simply check if theBest is null before using it. But, there are other alternatives to prevent the NullReferenceException when working with XOrDefault methods.

1. XOrDefault overloads from .NET 6.0

.NET 6.0 released some new LINQ methods and overloads.

With .NET 6.0, we can pass a second parameter to the XOrDefault() methods as a default value if the collection is empty or there are no matching elements. Like this,

var movies = new List<Movie>
{
    new Movie("Shrek", 2001, 3.95f),
    new Movie("Inside Out", 2015, 4.1f),
    new Movie("Ratatouille", 2007, 4f),
    new Movie("Toy Story", 1995, 4.1f),
    new Movie("Cloudy with a Chance of Meatballs", 2009, 3.75f)
};

var theBestOfAll = new Movie("My Neighbor Totoro", 1988, 5);

// With .NET 6.0 FirstOrDefault()
var theBest = movies.FirstOrDefault(
                    movie => movie.Rating == 5.0,
                    theBestOfAll);
                    // 👆👆👆
Console.WriteLine(theBest.Name);  // "My Neighbor Totoro"
//                👆👆👆
// We're safe here in any case

record Movie(string Name, int ReleaseYear, float Rating);
Enter fullscreen mode Exit fullscreen mode

Since we don't have any perfect movie in our catalog, FirstOrDefault() should return null. But, we're passing theBestOfAll as a custom default. We're safe to use the result of FirstOrDefault() without worrying about null and NullReferenceException.

2. Use Optional's XOrNone

Functional languages like F# or Haskell use a different approach for null. Instead of null, they use an Option or Maybe type.

With the Option type, we have a “box” that might have a value. It’s the same concept of nullable ints.

For example,

int? maybeAnInt = null;

var hasValue = maybeAnInt.HasValue;
// false

var dangerousInt = maybeAnInt.Value;
//                            👆👆
// Nullable object must have a value.

var safeInt = maybeAnInt.GetValueOrDefault();
// 0
Enter fullscreen mode Exit fullscreen mode

With nullable ints, we have a variable that either holds an integer or null. Also, we have the HasValue and Value properties and the GetValueOrDefault() method to access their inner value.

With the Option type, we extend the concept of a box with possibly a value to reference types. For example, if we write Option<Movie> maybeNull, we could have a movie or "nothing."

The Option type has two subtypes: Some represents a box with a value inside it, and None, an empty box.

Optional library

C# doesn't have a built-in Option type (yet?). We have to write our own or use a third-party library. Let's bring one: the Optional library, which offers "a robust option type for C#."

With the Optional library, to create a box with a movie, we write Option.Some(anyMovie); and to create an empty box to replace null, we write Option.None<Movie>().

For example,

using Optional;
//    👆

Option<Movie> someMovie = Option.Some(new Movie("Shrek", 2001, 3.95f));
Option<int> none = Option.None<Movie>();

var shrek = someMovie.Map(value => value.Name)
                     .ValueOr("Nothing");
// "Shrek"

var nothing = none.Map(value => value.Name)
                  .ValueOr("Nothing");
// "Nothing"
Enter fullscreen mode Exit fullscreen mode

To print a movie name, we first use the Map() method. It opens a box, transforms its value, and return a new box with the result. We wrote Map(value => value.Name) to grab a movie name.

Then, we used the ValueOr() method. It works like the GetValueOrDefault() of nullable ints. If the box doesn't have a value, it returns a default value instead.

XOrNone

The Optional library has the FirstOrNone(), LastOrNone() and SingleOrNone() methods instead of the usual XOrDefault(). When there isn't a matching element, they return None instead of null.

After that quick introduction to the Option type, let's use FirstOrNone() from the Optional library instead of FirstOrDefault(),

using Optional.Collections;
//    👆👆👆

var movies = new List<Movie>
{
    new Movie("Shrek", 2001, 3.95f),
    new Movie("Inside Out", 2015, 4.1f),
    new Movie("Ratatouille", 2007, 4f),
    new Movie("Toy Story", 1995, 4.1f),
    new Movie("Cloudy with a Chance of Meatballs", 2009, 3.75f)
};

var theBestOfAll = new Movie("My Neighbor Totoro", 1988, 5);

// With Optional's FirstOrNone()
var theBest = movies.FirstOrNone(movie => movie.Rating == 5.0)
                    // 👆👆👆
                    .ValueOr(theBestOfAll);
                    // 👆👆👆
Console.WriteLine(theBest.Name);  // My Neighbor Totoro

record Movie(string Name, int ReleaseYear, float Rating);
Enter fullscreen mode Exit fullscreen mode

Again, we don't have a perfect movie in our catalog, FirstOrNone returns None, an empty box. Then, with the ValueOr() method, we print our favorite movie.

When we use the XOrNone() methods, we're forced to check if they return something before using their result. We didn't have to worry about null.

Voilà! Those are two alternatives to prevent NullReferenceException when working with LINQ XOrDefault() methods: using .NET 6.0 new overloads and the Option type from Functional Languages.


Join my course C# NullReferenceException Demystified on Udemy and learn the principles, features, and strategies to avoid this exception in just 1 hour and 5 minutes.

Happy coding!

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up