In the specific case, calling First
and Where
on a string[]
, the methods called are the Enumerable.Where
and Enumerable.First
extension methods.
Enumerable.Where
does this:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
// null checks omitted
if (source is TSource[])
return new WhereArrayIterator<TSource>((TSource[])source, predicate);
//the rest of the method will not execute
}
and the constructor of WhereArrayIterator
does just:
public WhereArrayIterator(TSource[] source, Func<TSource, bool> predicate) {
this.source = source;
this.predicate = predicate;
}
So nothing is actually done here, except to create an iterator.
The first First
method, without a predicate does this:
public static TSource First<TSource>(this IEnumerable<TSource> source) {
//null check
IList<TSource> list = source as IList<TSource>;
if (list != null) {
//this branch is not taken as string[] does not implement IList<string>
if (list.Count > 0) return list[0];
}
else {
//this is actually the WhereArrayIterator from before
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext())
return e.Current;
}
}
throw Error.NoElements();
}
However, the second First
does this
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
//null checks
foreach (TSource element in source) {
if (predicate(element)) return element;
}
throw Error.NoMatch();
}
Which in the case of an array, is as fast as direct linear access.
In a nutshell, this means that calling First(predicate)
on an array will be somewhat faster, by a not large, but still detectable factor. This might not hold for lists, and will certainly not hold for IQueryable
objects, that are a completely different story.
However, this is micro-optimization at it's worst. Unless this is done millions of times, it won't save too many seconds. Even as I know this now, I'll still use whatever is clearer to read and understand.