using LINQ where(lamdaexp).First and First(lambdaexp)

本文关键字:First and lambdaexp lamdaexp LINQ where using | 更新日期: 2023-09-27 18:04:46

以下两种方式有什么区别吗?我得到相同的输出,但我试图理解哪个是正确和有效的

方法1:

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

方法2:

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

using LINQ where(lamdaexp).First and First(lambdaexp)

(我假设你正在使用Linq to .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);

根据First()的源代码,我们可以说,First()将遍历集合,并在找到满足条件的集合中的第一项时停止迭代。

  • products.Where(p => p.ProductID == 12).First();
  • 首先在Where方法中创建满足条件的元素迭代器。然后,它会把第一个元素添加到迭代器中。而且,将在找到后立即返回第一个元素。

    作为额外的注意,LINQ在一些方法中使用延迟执行。这与你的问题的结果有一定的关系。

    延迟执行是执行模型的一种模式CLR确保只在需要时提取值基于ienumerable的信息源。当任意Linq算子使用延迟执行,CLR封装相关的信息,如原始序列、谓词或选择器(如果(Any)放入迭代器中,当信息为使用ToList方法从原始序列中提取或手动使用底层的GetEnumerator和c#中的MoveNext方法。

    问题是。哪个更快?


    主要是Where().FirstFirst快。ListArray 。否则,First()将更快。以下是@Akash Kava回答中的详细解释。

    让我们关注Where()的实现。如果您的集合是List,它将返回WhereListIterator(),但First()将只迭代源。在我看来,他们在WhereListIterator实现方面做了一些加快。在此之后,我们调用First()方法,该方法不接受谓词作为输入,只迭代过滤后的集合。

    而且,据我所知, Where迭代器避免间接调用虚表,而是直接调用迭代器方法。这就是加速的原因。

    我刚刚运行了一个测试,你可以在这里看到。netfiddle的结果,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,总是,First比Where First快。

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

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

    然而,缓存WhereListIterator的操作有一定的成本,所以对于较小的集合和产生较小结果的某些条件,Where(). first()会更慢。

    如果你看这个例子,First每次都胜过Where First。

    https://dotnetfiddle.net/OrUUSG

    它们在功能上是相等的,同样有效。LINQ构建了一个查询,在结果被枚举之前不会求值,例如foreach循环或本例中的First()操作。

    因此两者都将按原顺序计算条目,直到找到第一个具有ProductID == 12的实例,如果找到则返回该实例(如果没有则抛出异常)。

    也就是说,后一种方法是我喜欢的方法,因为它更简洁,通常更容易阅读。

    我觉得你的问题更像是:

    1. Where会在到达First之前迭代整个集合吗?
    2. First也会这样做吗?

    我已经写了自己的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处理迭代器时会发生什么。