选择后跟Where在IEnumerable上进行两次迭代吗?

时间:2011-11-02 22:34:25

标签: c# performance linq linq-to-objects

我说我有

IEnumerable<int> list = new int[] { 1, 2, 3 };
List<int> filtered = list.Select(item => item * 10).Where(item => item < 20).ToList();

问题是有两次迭代还是只有一次。

换句话说,性能等同于:

IEnumerable<int> list = new int[] { 1, 2, 3 };
List<int> filtered = new List<int>();
foreach(int item in list) {
    int newItem = item * 10;
    if(newItem < 20)
        filtered.Add(newItem);
}

2 个答案:

答案 0 :(得分:7)

当您调用.ToArray方法时,对集合执行了一次迭代,因此两者都应该是等效的。 .Select是一个投影,.Where是一个过滤器,都表示为原始数据集上的表达式树。

可以很容易地证明:

public class Foo: IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        yield return 1;
        Console.WriteLine("we are at element 1");
        yield return 2;
        Console.WriteLine("we are at element 2");
        yield return 3;
        Console.WriteLine("we are at element 3");
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

class Program
{
    static void Main()
    {
        var filtered = new Foo()
            .Select(item => item * 10)
            .Where(item => item < 20)
            .ToList();
    }
}
运行时

打印以下内容:

we are at element 1
we are at element 2
we are at element 3

答案 1 :(得分:2)

在Linq to Objects中,WHERE和SELECT不会迭代枚举。调用代码在对查询或ToList或ToArray()等执行foreach时枚举它。

在Linq to SQL中,没有任何迭代。当您执行ToList或ToArray()时,查询将由数据库执行。根据查询类型,db可以查找索引或执行表扫描。