为什么IEnumerable.Count()重新评估查询?

当我期望“1 1 1 1”时,以下代码打印“2 2 2 2”。 为什么“Count()”会重新评估查询?

class Class1 { static int GlobalTag = 0; public Class1() { tag = (++GlobalTag); } public int tag; public int calls = 0; public int Do() { calls++; return tag; } } class Program { static void Main(string[] args) { Class1[] cls = new Class1[] { new Class1(), new Class1(), new Class1(), new Class1() }; var result = cls.Where(c => (c.Do() % 2) == 0); if (result.Count() <= 10) { if (result.Count() <= 10) { foreach (var c in cls) { Console.WriteLine(c.calls); } } } } } 

它还有什么用呢? 你期望Count()做什么来缓存值?

LINQ to Objects通常懒惰地执行,只在需要时实际评估查询 – 例如计算元素。 因此对Where的调用根本不是评估序列; 它只记得谓词和序列,以便它可以在需要时对其进行评估。

有关LINQ to Objects如何工作的更多详细信息,我建议您阅读我的Edulinq博客系列 。 它相当长(并没有完全完成),但它会让你更深入地了解LINQ to Objects的工作原理。

并非所有序列都是可重复的,因此通常必须对它们进行计数。 为了帮助它,您可以在序列上调用ToList() – 即使键入为IEnumerable ,LINQ-to-Objects仍然会快捷并使用.Count – 因此非常便宜且可重复。

有关不可重复序列的示例:

 static int evil; static IEnumerable GetSequence() { foreach(var item in Enumerable.Range(1, Interlocked.Increment(ref evil))) yield return item; } 

与演示:

 var sequence = GetSequence(); Console.WriteLine(sequence.Count()); // 1 Console.WriteLine(sequence.Count()); // 2 Console.WriteLine(sequence.Count()); // 3