即使在Select
运算符之前调用了Take
运算符,也只会为前两个输入元素调用lambda表达式:
string[] count = { "one ", "two ", "three ", "four ", "five ", "six " };
IEnumerable<int> results = count.Select(item =>
{
Console.WriteLine(item);
return item.Length;
}).Take(2);
foreach (int i in results);
输出:
one two
a)假设我们将几个Linq-to-Objects标准查询运算符链接在一起,Take
运算符被调用的顺序从不重要?因此,调用lambda表达式的输入元素的数量始终是相同的,Take
是先调用还是最后调用永远不重要?
b)再次假设我们将几个Linq-to-Objects标准查询运算符链接在一起,是否还有其他Linq-to-Objects运算符,其中调用它们的顺序不会影响其输入元素的数量lambda表达式被调用?
谢谢
答案 0 :(得分:1)
查询运算符通常只在源集合中向前移动一步,因为它们需要产生所需的输出结果数。
在您的示例中,Take()
只需要Select()
中的2个项目,以便Select()
生成所有内容。 Select()
可能大致如此实现(取自Jon Skeet's blog:
private static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
foreach (TSource item in source)
{
yield return selector(item);
}
}
对运算符进行排序和分组需要先创建完整的结果集,然后才能返回任何内容(例如OrderBy()
)。
只要您不使用任何排序,分组,过滤(例如使用Where()
或Skip()
)或任何其他操作员更改序列中的项目数量,就无所谓在链中你放Take()
。
答案 1 :(得分:1)
订单很重要,但执行通常是推迟的,毫无疑问是优化的。如果你的断言/观察是正确的,那么在Take()之前添加ToList()不会影响输出,但确实如此。 ToList()调用用于在Take()之前强制完全枚举,并导致在所有元素上调用Select lambda。
这里的教训是不依赖于优化的正确性。
string[] count = { "one ", "two ", "three ", "four ", "five ", "six " };
IEnumerable<int> results = count.Select(item =>
{
Console.WriteLine(item);
return item.Length;
})
.ToList()
.Take(2);