FirstOrDefault()关闭LINQ与FirstOrDefault()的Lambda?

时间:2010-01-27 15:43:00

标签: linq .net-3.5

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

我倾向于后者,作为IMO,它使代码更清晰。但我很好奇,对于那些正在发生的事情的技术“胆量”是否更有效率。如果您使用不同类型的IEnumerables,这会改变吗?像DataTables或字符串数​​组或LINQ对象?

======编辑==========

假设Jon Skeet的帖子是正确的,我去了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 =&gt; 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);
}

看着这仍然让我有点困惑,通过对某些对象类型使用适当的迭代器会增加效率吗?或者是否更有效地跳过所有这些并开始循环并测试?我的直觉再次告诉我它是后者。

3 个答案:

答案 0 :(得分:18)

好吧,编辑器会删除“select”部分,所以你真的在比较:

foo.Where(f => f.Bar == "spider monkey")
   .FirstOrDefault()

VS

foo.FirstOrDefault(f => f.Bar == "spider monkey")

我怀疑它会在LINQ to Objects中对效率产生任何显着的差异。我本人亲自使用后一版本...除非我想在其他地方重用查询的过滤部分。

答案 1 :(得分:4)

我更喜欢后者,因为它更简洁。

效率条款我怀疑你能找到它们之间的实质性差异。它们的行为几乎完全相同,即使在实践中它们的工作略有不同。

最大的区别是第一个实例创建了一个新的IEnumerable<T>实例,然后为第一个元素进行了操作。在后一种情况下,现有IEnumerable<T>实例将与第一个匹配谓词的值一起走动。再一次,不太可能被注意到。

答案 2 :(得分:-1)

第二个版本会更有效率。

第一个版本

var foo1 = (from f in foos
     where f.Bar == "spider monkey"
     select f).FirstOrDefault();

将始终遍历所有项目,创建匹配项目的新集合。

第二个版本

var foo2 = foos.FirstOrDefault(f => f.Bar == "spider monkey");

只会遍历项目,直到找到匹配项,然后返回。