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

C# 使用LINQ where(lamdaexp).First和First(lambdaexp),c#,linq,lambda,C#,Linq,Lambda,以下两种方法有什么不同吗?我得到了同样的结果,但我正在努力理解哪一个是正确的和有效的 方法1: Product product12 = products.Where(p => p.ProductID == 12).First(); 方法2: Product prod12 = products.First(p => p.ProductID == 12); 它们在功能上是等价的,同样有效。LINQ构建一个查询,在枚举结果之前不会对其求值,例如foreach循环或在本例中是First(

以下两种方法有什么不同吗?我得到了同样的结果,但我正在努力理解哪一个是正确的和有效的

方法1:

Product product12 = products.Where(p => p.ProductID == 12).First();
方法2:

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

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

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

也就是说,后一种方法是我首选的方法,因为它更简洁,而且通常更易于阅读。

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

这是:

让我们来找出每种代码的作用:

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

  • products.Where(p=>p.ProductID==12.First()
  • 首先,它将在
    Where
    方法中创建迭代器,其中元素满足条件。然后,它再次将get第一个元素添加到该迭代器中。并且,将在找到第一个元素时再次返回该元素

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

    延迟执行是执行模型的一种模式 CLR确保仅在需要时从中提取值 基于IEnumerable的信息源。当任何Linq运算符 使用延迟执行,CLR封装相关的 信息,例如原始序列、谓词或选择器(如果 any)转换为一个迭代器,在生成信息时将使用该迭代器 使用ToList方法从原始序列中提取,或 ForEachmethod或手动使用基础GetEnumerator和 C#中的MoveNext方法

    问题是哪一个更快?


    要点是,
    Where().First
    First
    快。如果是
    列表
    数组
    。否则,
    First()
    会更快

    让我们关注
    Where()
    实现。如果您的集合是List,它将返回
    WhereListIterator()
    ,但
    First()
    将只在源代码上迭代。 在我看来,他们在
    WhereListIterator
    的应用程序中取得了一些进展。在此之后,我们将调用
    First()
    方法,该方法不接受任何谓词作为输入,只对过滤后的集合进行迭代


    另外,据我所知,其中的
    迭代器避免了间接的虚拟表调用,而是直接调用迭代器方法。这就是加快速度的原因。

    我认为您的问题更像是:

  • 在第一次进入
    之前,将
    其中
    迭代整个集合吗
  • 首先也会这样做吗
  • 我已经为
    编写了自己的实现,其中
    首先

    这个示例不是确切的.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");
        }
    }
    
    公共静态类扩展
    {
    公共静态IEnumerable MyWhere(此IEnumerable集合,Func条件)
    {
    foreach(集合中的var项)
    {
    如果(条件(项目))
    {
    收益回报项目;
    }
    }
    }
    公共静态T MyFirst(此IEnumerable集合,Func条件)
    {
    foreach(集合中的var项)
    {
    如果(条件(项目))
    {
    退货项目;
    }
    }
    抛出新的InvalidOperationException(“未找到元素”);
    }
    }
    
    执行以下代码:

    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);
    
    List myList=新列表{1,2,3,4,5,6,7,8,9,10};
    var-five=myList.MyWhere(i=>i<6).MyFirst(i=>i==5);
    

    MyWhere
    MyFirst
    foreach
    中放置一个调试器断点,开始了解使用Linq To对象处理迭代器时发生的情况。

    我刚刚运行了一个测试,您可以在.Net FIDLE上看到结果

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

        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
    
    Func filter=x=>x.Size==200;
    运行(“第一个”,()=>items.First(过滤器));
    运行(“Where First”,()=>items.Where(filter.First());
    第一次为88毫秒
    69毫秒,第一个在哪里
    过滤器=x=>x。尺寸<200;
    运行(“第一个”,()=>items.First(过滤器));
    运行(“Where First”,()=>items.Where(filter.First());
    第一次为2毫秒
    4毫秒,第一个在哪里
    过滤器=x=>x。尺寸>200;
    运行(“第一个”,()=>items.First(过滤器));
    运行(“Where First”,()=>items.Where(filter.First());
    第一次为88毫秒
    71毫秒,第一个在哪里
    
    因此没有明确的答案

    另一个测试,一直以来,First比Where First快

    更新

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

    在列表的Where()中使用的WhereListIterator不会创建新的枚举数,而是在线程中缓存当前迭代器。这使得Where().First()运行更快

    但是,这种缓存ListIterator的操作有一些缺点
    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);
    
        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