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
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.