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");

будет только циклически перемещать элементы до тех пор, пока совпадение не будет найдено и не будет возвращено.