无论何时调用Linq-to-Objects操作符,处理的输入元素的数量是否相同?

时间:2011-12-06 19:10:00

标签: c# .net linq-to-objects

即使在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表达式被调用?

谢谢

2 个答案:

答案 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);