1

我知道Count()LINQ 提供的方法有一个优化,它会检查源序列是否实现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因为该序列不包含任何元素。

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

4

3 回答 3

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
于 2012-06-30T18:02:25.000 回答
1

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

文档没有提到任何类似的东西......

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

于 2012-06-30T18:00:24.907 回答
1

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

于 2012-06-30T18:02:00.267 回答