Problem
When you call .First() on an empty IEnumerable, you get the following exception:
System.InvalidOperationException: Sequence contains no elements
Solution
Option 1 – Use .FirstOrDefault() instead of .First()
When the IEnumerable is empty, .FirstOrDefault() returns the default value for the type.
IEnumerable<IMessage> data = GetData();
var first = data.FirstOrDefault();
Console.WriteLine($"First = {first == default(IMessage)}");
Code language: C# (cs)
For reference types this returns null. For value types this returns 0 or that type’s equivalent of 0.
Option 2 – Use .Any() to check if the IEnumerable is empty
Instead of dealing with the default value (which is either null or 0), you can use .Any() to check if the IEnumerable is empty.
IEnumerable<IMessage> data = GetData();
if(data.Any())
{
var first = data.First();
Console.WriteLine($"First = {first}");
}
else
{
Console.WriteLine("There's no data");
}
Code language: C# (cs)
Option 3 – Get your own default object from .FirstOrDefault()
Create an extension method that returns a default object you specify.
static class IEnumerableExtension
{
public static T FirstOrDefault<T>(this IEnumerable<T> list, T defaultObj)
{
if (!list.Any())
return defaultObj;
else
return list.First();
}
}
Code language: C# (cs)
With reference types you can use the Null Object pattern (an object that does nothing). The benefit of this you don’t need to deal with null checking. In my case, I’m setting the default to EmptyMessage.
IEnumerable<IMessage> data = GetData();
var first = data.FirstOrDefault(new EmptyMessage());
Code language: C# (cs)
With value types you can specify a value that has special meaning in your system.
IEnumerable<int> intData = GetIntData();
var firstInt = intData.FirstOrDefault(-1);
Code language: C# (cs)