Why does LINQ have so many ways to return one element?

Published: Tuesday 26 December 2023

A LINQ query in C# is frequently used as a way of returning results from an enumerable type like a list.

However, there are a number of ways of returning a single result. We'll explore each way and perform some benchmarking to see which one might be best to use in a .NET project.

Exploring the different ways to get one element

There are four different ways of getting a single result from a LINQ query.

C# coding challenges

C# coding challenges

Our online code editor allows you to compile the answer.

To explore each one, we've set up a MyClass class and will add three elements to a list to test this out.

public class MyRecord
{
	public int Id { get; }

	public MyRecord(int id)
	{
		Id = id;
	}
}
var myRecords = new List<MyRecord>
{
    new(1),
    new(2),
    new(3)
};

First and FirstOrDefault

Using the First method will return the first record from a LINQ query. It doesn't matter if there is more than one result, it will always return the first. 

However, if there are no results, it will throw the following exception:

System.InvalidOperationException: 'Sequence contains no matching element'

This exception can be avoided by using the FirstOrDefault method. When it's used and there are no results, it will return NULL as the output.

var myRecords = new List<MyRecord>
{
    new(1),
    new(2),
    new(3)
};

Console.WriteLine(myRecords.First(x => x.Id == 2).Id); // Outputs "2"
Console.WriteLine(myRecords.First(x => x.Id == 4).Id); // Throws exception
Console.WriteLine(myRecords.First().Id); // Outputs "1"
Console.WriteLine(myRecords.FirstOrDefault(x => x.Id == 4)?.Id); // Outputs ""

Single and SingleOrDefault

Using the Single method must return one record from a LINQ query. If there is more than one result, it will throw the following exception:

System.InvalidOperationException: 'Sequence contains more than one element'

And if there are no records returned, it will throw the following exception:

System.InvalidOperationException: 'Sequence contains no matching element'

To avoid the exception being thrown on no results, the SingleOrDefault method can be used. This will return NULL if there are no results. However, an exception will still be thrown if there is more than one result.

var myRecords = new List<MyRecord>
{
    new(1),
    new(2),
    new(3)
};

Console.WriteLine(myRecords.Single(x => x.Id == 2).Id); // Outputs "2"
Console.WriteLine(myRecords.Single(x => x.Id == 4)?.Id); // Throws exception
Console.WriteLine(myRecords.Single().Id); // Throws exception
Console.WriteLine(myRecords.SingleOrDefault(x => x.Id == 4).Id); // Outputs ""

Benchmarking

We set up benchmarking for each of these methods and here are the results:

Method Mean Allocated
First 21.97 ns 40 B
FirstOrDefault 20.25 ns 40 B
Single 33.97 ns 40 B
SingleOrDefault 31.49 ns 40 B

We can see that the allocated memory for each one is the same.

In-terms of the mean, the FirstOrDefault and First perform faster than the Single and SingleOrDefault methods by over 10 ns. And that makes sense as the single methods need to check if there is more than one element in the query hence the additional overhead.

Watch our video where we test each of these methods out and analyse the full results of the benchmarking.

The differences

So the main difference is that the Single method only expects one record and will return an exception if there are more.

Whereas with the First method, it will return the first record from the LINQ query even if there is more than one record.

Both will return an exception if there are no records, but this can be returned as NULL if either the FirstOrDefault or SingleOrDefault methods are used.