我知道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>
。依靠这种行为是不明智的吗?
答案 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?
文档没有提及任何类似的内容......
答案 2 :(得分:1)
这是您无法使用的IEnumerator<T>
。但是,Count和First各自创建一个单独的枚举器(通过调用GetEnumerator
上的IEnumerable<T>
)。