使用LINQ where(lamdaexp).First和First(lambdaexp)

时间:2015-07-26 04:57:40

标签: c# linq lambda

以下两种方法有什么不同吗?我得到相同的输出,但我试图了解哪个是正确有效的

接近1:

Product product12 = products.Where(p => p.ProductID == 12).First();

方法2:

Product prod12 = products.First(p => p.ProductID == 12);

4 个答案:

答案 0 :(得分:7)

(我假设你使用的是Linq到.Net)
首先让我们来看看它们的源代码:

以下是Where()

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

这是First()

   public static TSource First<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 element in source) {
                if (predicate(element)) return element;
            }
            throw Error.NoMatch();
        }

让我们找一下每个代码的作用:

  1. products.First(p => p.ProductID == 12);
  2. 根据First()我们可以说的First()的源代码,products.Where(p => p.ProductID == 12).First();将迭代收集,并在找到符合条件的集合中的第一个项目时停止迭代。

    1. Where
    2. 首先,它将在Where().First方法中创建迭代器,其中元素符合条件。然后,它将再次添加获取该迭代器的第一个元素。并且, 一旦找到它就会返回第一个元素

      作为补充说明,LINQ在某些方法中使用延迟执行。它与你的问题的结果有一些关系。

        

      延迟执行是执行模型的一种模式   CLR确保仅在需要时才提取值   基于IEnumerable的信息源。当任何Linq运营商   使用延迟执行,CLR封装相关的   信息,例如原始序列,谓词或选择器(如果   任何),进入迭代器,将在信息时使用   使用ToList方法或原始序列提取   ForEachmethod或手动使用底层的GetEnumerator和   C#中的MoveNext方法。

      而且,问题是。 哪一个更快?

      重点是,FirstList更快。如果是ArrayFirst() 。否则,Where()会更快。 Here is the detailed exlpanation in @Akash Kava answer

      让我们关注WhereListIterator()实施。如果您的集合是List,它将返回First(),但WhereListIterator只会迭代源代码。 在我看来,他们在First()的{​​{3}}中加快了速度。在此之后,我们调用Where方法,该方法不接受任何谓词作为输入,只会迭代过滤后的集合。

      另外,据我所知, You can do this - remove("drive\\folder\\foo.txt"): 迭代器避免了间接虚拟表调用,但直接调用了迭代器方法。 而且,这就是这个原因。加速。

答案 1 :(得分:1)

我刚刚进行了测试,你可以在.Net小提琴上看到结果,https://dotnetfiddle.net/3lCqsR

真正的答案是,更快取决于所应用的过滤器类型和迭代的集合类型。

    Func<Item,bool> filter = x => x.Size == 200;

    Run("First", () => items.First( filter ));
    Run("Where First", () => items.Where( filter ).First());

88 milliseconds for First
69 milliseconds for Where First


    filter = x => x.Size < 200;

    Run("First", () => items.First( filter ));
    Run("Where First", () => items.Where( filter ).First());

2 milliseconds for First
4 milliseconds for Where First


    filter = x => x.Size > 200;

    Run("First", () => items.First( filter ));
    Run("Where First", () => items.Where( filter ).First());

88 milliseconds for First
71 milliseconds for Where First

所以没有明确的答案。

另一项测试,https://dotnetfiddle.net/k11nX6,始终是第一次比第一次更快。

<强>更新

在分析List类之后,发现Where()。First()仅比First()更快,仅在List和Array的情况下,它不适用于IQueryable或任何其他形式的Enumerable。原因是,List中使用的枚举器不是每个线程都缓存的。 List总是创建新的Enumerator,因此First()使用List的Enumerator(迭代器)。

WhereListIterator在Where()for List中使用,不创建新的Enumerator,而是在线程中缓存当前的Iterator。这使得Where()。First()运行得更快。

但是,这种缓存WhereListIterator的操作有一些成本,因此对于较小的集合和一些产生较小结果的条件,Where()。First()较慢。

如果你看一下这个例子,那么每次首先击败Where First。

https://dotnetfiddle.net/OrUUSG

答案 2 :(得分:0)

它们功能相同且同样有效。 LINQ构建一个查询,该查询在枚举结果之前不进行评估,例如一个foreach循环或在这种情况下是First()操作。

因此,两者都将按照原始顺序评估项目,直到找到ProductID == 12的第一个实例并在找到时返回该实例(如果没有则抛出异常)。

那说后一种方法是我的首选方法,因为它更简洁,通常更容易阅读。

答案 3 :(得分:0)

我认为你的问题更像是:

  1. Where之前First会迭代整个集合吗?
  2. First也会这样做吗?
  3. 我已将自己的实现编写到WhereFirst
    这个示例不是确切的.net实现,但是它的工作原理相同。

    public static class Extensions
    {
        public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> collection, Func<T,bool> condition)
        {
            foreach(var item in collection)
            {
                if(condition(item))
                {
                    yield return item;
                }
            }
        }
    
        public static T MyFirst<T>(this IEnumerable<T> collection, Func<T,bool> condition)
        {
            foreach (var item in collection)
            {
                if (condition(item))
                {
                    return item;
                }
            }
    
            throw new InvalidOperationException("No element found");
        }
    }
    

    执行此代码:

    List<int> myList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    var five = myList.MyWhere(i => i < 6).MyFirst(i => i == 5);
    

    MyWhereMyFirst foreach中放置一个调试器断点,并开始了解使用Linq To Objects处理迭代器时会发生什么。