The first option can be broken as a result of a null
item that passes the check, thus making you think there are no matching items, even if they are. It doesn't apply to this particular example, but it could apply in the general case.
However, the second example here iterates the source sequence twice (sometimes), once to see if there are any results and then again to get that result. If the source sequence needs to perform a database query to get the results that could be very expensive. Because of this you should only use this option if you're sure that you have an in-memory collection you're dealing with and that it's not particularly large (or that the first item you need will be found quickly).
In the event that you need to worry about this particular edge case with the first option, or you want to get the benefits of using Any
and First
(for their superior semantic representation of what you want) with the performance benefits of FirstOrDefault
you can use this pattern:
var myVar = myCollection.Where(q => q.Id == 10)
.Take(1)
.ToList();
if (myVar.Any())
{
anotherVar = myVar.First().MyName;
}
You could make an extension method to shorten this if you wanted:
public static IEnumerable<T> FirstOrEmpty<T>(this IEnumerable<T> source)
{
//TODO: null check arguments
using (var iterator = source.GetEnumerator())
{
if (iterator.MoveNext())
return new T[] { iterator.Current };
else
return Enumerable.Empty<T>();
}
}
public static IEnumerable<T> FirstOrEmpty<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
return FirstOrEmpty(source.Where(predicate));
}
This would allow you to just write:
var myVar = myCollection.FirstOrEmpty(q => q.Id == 10);
if (myVar.Any())
{
anotherVar = myVar.First().MyName;
}