以下两种方法有什么不同吗?我得到相同的输出,但我试图了解哪个是正确有效的
接近1:
Product product12 = products.Where(p => p.ProductID == 12).First();
方法2:
Product prod12 = products.First(p => p.ProductID == 12);
答案 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();
}
让我们找一下每个代码的作用:
products.First(p => p.ProductID == 12);
根据First()
我们可以说的First()
的源代码,products.Where(p => p.ProductID == 12).First();
将迭代收集,并在找到符合条件的集合中的第一个项目时停止迭代。
Where
首先,它将在Where().First
方法中创建迭代器,其中元素符合条件。然后,它将再次添加获取该迭代器的第一个元素。并且, 一旦找到它就会返回第一个元素 。
作为补充说明,LINQ在某些方法中使用延迟执行。它与你的问题的结果有一些关系。
延迟执行是执行模型的一种模式 CLR确保仅在需要时才提取值 基于IEnumerable的信息源。当任何Linq运营商 使用延迟执行,CLR封装相关的 信息,例如原始序列,谓词或选择器(如果 任何),进入迭代器,将在信息时使用 使用ToList方法或原始序列提取 ForEachmethod或手动使用底层的GetEnumerator和 C#中的MoveNext方法。
而且,问题是。 哪一个更快?
重点是,First
比List
更快。如果是Array
和First()
。否则,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。
答案 2 :(得分:0)
它们功能相同且同样有效。 LINQ构建一个查询,该查询在枚举结果之前不进行评估,例如一个foreach循环或在这种情况下是First()操作。
因此,两者都将按照原始顺序评估项目,直到找到ProductID == 12的第一个实例并在找到时返回该实例(如果没有则抛出异常)。
那说后一种方法是我的首选方法,因为它更简洁,通常更容易阅读。
答案 3 :(得分:0)
我认为你的问题更像是:
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处理迭代器时会发生什么。