Why is IEnumerable.Count () overestimating a request?

The following code prints "2 2 2 2" when I expect "1 1 1 1". Why does "Count ()" overestimate the request?

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); } } } } } 
+4
source share
2 answers

How else could this work? What would you expect Count() to do to cache values?

LINQ to Objects is usually executed lazily, only actually evaluates the query when it needs to - for example, to count elements. Therefore, the Where call does not evaluate the sequence at all; he simply remembers the predicate and sequence so that he can evaluate it when he needs to.

For more on how LINQ to Objects works, I suggest you read my Edulinq blog series . It's quite long (and not quite finished), but it will give you much more information on how LINQ to Objects works.

+10
source

Not all sequences are repeatable, so they usually need to be counted. To help it, you can call ToList() in a sequence - even if typed as IEnumerable<T> , LINQ-to-Objects will still shrink and use .Count - so very cheap and repeatable.

For an example of a non-repeatable sequence:

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

with demo:

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

Source: https://habr.com/ru/post/1340428/


All Articles