为什么不从LINQ Where()方法返回的IEnumerable完全由Count()消耗?

时间:2012-06-30 17:54:28

标签: c# linq linq-to-objects

我知道LINQ提供的Count()方法有一个优化,它会检查源序列是否实现ICollection<T>,如果是,则调用Count属性而不是迭代整个系列。使用此优化时,不会消耗基础IEnumerable<T>,因此可以在Count()之后的其他调用中使用它。

值得注意的是,接受谓词的Count的重载不执行这样的优化,因为它必须检查每个元素的值。

现在考虑以下完整程序:

using System;
using System.Collections.Generic;
using System.Linq;

namespace count_where_issue
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<int> items = new List<int> {1, 2, 3, 4, 5, 6};
            IEnumerable<int> evens = items.Where(y => y % 2 == 0);
            int count = evens.Count();
            int first = evens.First();
            Console.WriteLine("count = {0}", count);
            Console.WriteLine("first = {0}", first);
        }
    }
}

打印,

count = 3
first = 2

我的期望是Count需要使用evens返回的整个Where()序列,随后对evens.First()的调用将失败InvalidOperationException因为序列将包含没有元素。

为什么这个程序按照它的方式工作?在调用`Count()之后,我通常不会尝试使用IEnumerable<T>。依靠这种行为是不明智的吗?

3 个答案:

答案 0 :(得分:2)

我不知道这是不是您的意思,但evens.Count()evens.First()都列举了items.Where(...)

您可以在以下输出中看到这一点:

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<int> items = new List<int> { 1, 2, 3, 4, 5, 6 };
        IEnumerable<int> evens = items.Where(y => isEven(y));

        int count = evens.Count();
        Console.WriteLine("count = {0}", count);

        int first = evens.First();
        Console.WriteLine("first = {0}", first);

    }

    private static bool isEven(int y)
    {
        Console.Write(y + ": ");

        bool result = y % 2 == 0;

        Console.WriteLine(result);    
        return result;
    }
}

这是:

1: False
2: True
3: False
4: True
5: False
6: True
count = 3
1: False
2: True
first = 2

答案 1 :(得分:1)

是什么让你觉得Count()会以任何方式影响原始的IEnumerable?

文档没有提及任何类似的内容......

http://msdn.microsoft.com/en-us/library/bb338038.aspx

答案 2 :(得分:1)

这是您无法使用的IEnumerator<T>。但是,Count和First各自创建一个单独的枚举器(通过调用GetEnumerator上的IEnumerable<T>)。