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);
(我假设你正在使用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();
}
让我们来看看每个代码是做什么的:
-
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().First
比First
快。List
和Array
。否则,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的实例,如果找到则返回该实例(如果没有则抛出异常)。
也就是说,后一种方法是我喜欢的方法,因为它更简洁,通常更容易阅读。
我觉得你的问题更像是:
-
Where
会在到达First
之前迭代整个集合吗? -
First
也会这样做吗?
我已经写了自己的Where
和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");
}
}
执行以下代码:
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);
在MyWhere
和MyFirst
foreach
中放置一个调试器断点,并开始理解使用Linq To Objects处理迭代器时会发生什么。