FirstOrDefault() от LINQ против FirstOrDefault() с помощью Lambda?
Мне немного любопытно, что считается "лучшей практикой", когда дело доходит до FirstOrDefault.
Я уже видел этот вопрос, который похож на вопрос, который у меня есть, но недостаточно близко, чтобы он отвечал на мой вопрос.
Какой из них - "лучший код"? и почему?
var foos = GetMyEnumerableFoos();
var foo1 = (from f in foos
where f.Bar == "spider monkey"
select f).FirstOrDefault();
/* OR */
var foo2 = foos.FirstOrDefault(f => f.Bar == "spider monkey");
Я склоняюсь к последнему, как ИМО, он делает для более чистого кода. Но мне любопытно, действительно ли технологический "кишок" того, что происходит там, более эффективен по-другому. Это изменяется, если вы используете разные типы IEnumerables? например, DataTables или строковые массивы или объекты LINQ?
========= редактировать ==========
Предполагая, что сообщение Джона Скита верное, я просмотрел Reflector, чтобы увидеть, как выглядят Where и FirstOrDefault, и вот что я придумал:
В случае foos.Where(f = > f.Bar == "паука-обезьяна" ). FirstOrDefault()
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
if (predicate == null)
{
throw Error.ArgumentNull("predicate");
}
if (source is Iterator<TSource>)
{
return ((Iterator<TSource>) source).Where(predicate);
}
if (source is TSource[])
{
return new WhereArrayIterator<TSource>((TSource[]) source, predicate);
}
if (source is List<TSource>)
{
return new WhereListIterator<TSource>((List<TSource>) source, predicate);
}
return new WhereEnumerableIterator<TSource>(source, predicate);
}
который будет загружаться в:
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
if (list.Count > 0)
{
return list[0];
}
}
else
{
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
return enumerator.Current;
}
}
}
return default(TSource);
}
В случае foos.FirstOrDefault(f = > f.Bar == "паука-обезьяна" );
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
if (predicate == null)
{
throw Error.ArgumentNull("predicate");
}
foreach (TSource local in source)
{
if (predicate(local))
{
return local;
}
}
return default(TSource);
}
Глядя на то, что все еще оставляет меня в замешательстве, можно ли повысить эффективность, используя правильный итератор для определенных типов объектов? Или более эффективно пропустить все это и просто начать цикл и тестирование? Моя кишка снова говорит мне, что это последняя.
Ответы
Ответ 1
Ну, часть "select" будет удалена компилятором в любом случае, поэтому вы действительно сравниваете:
foo.Where(f => f.Bar == "spider monkey")
.FirstOrDefault()
против
foo.FirstOrDefault(f => f.Bar == "spider monkey")
Я сомневаюсь, что в любом случае он будет иметь какое-либо существенное значение для эффективности в LINQ для объектов. Я лично лично использовал бы последнюю версию... если бы я не хотел повторно использовать фильтрующую часть запроса в другом месте.
Ответ 2
Я предпочитаю последнее просто потому, что он более краткий.
Это условия эффективности. Я сомневаюсь, что вы могли бы найти существенную разницу между ними. Они практически идентичны поведению, хотя на практике они работают немного иначе.
Самое большое отличие состоит в том, что первый создает новый экземпляр IEnumerable<T>
, который затем перемещается для первого элемента. В последнем случае существующий экземпляр IEnumerable<T>
выполняется для первого значения, соответствующего предикату. Опять же, очень маловероятно, чтобы его заметили.
Ответ 3
Вторая версия будет намного более эффективной.
Первая версия
var foo1 = (from f in foos
where f.Bar == "spider monkey"
select f).FirstOrDefault();
Всегда будет проходить через все элементы, создавая новую коллекцию соответствующих элементов.
Вторая версия
var foo2 = foos.FirstOrDefault(f => f.Bar == "spider monkey");
будет только циклически перемещать элементы до тех пор, пока совпадение не будет найдено и не будет возвращено.