https://blog.usejournal.
com/enumeration-in-net-d5674921512e
Enumerating
The simplest way to enumerate is to use a foreach loop. Lets start
with a simple method that takes an IEnumerable<int> and outputs
all its items to the console:
You can check at SharpLab that, in reality, the above code is
expanded to awhile loop. Focusing on the enumeration, we can
resume it to this:
Notice that, first a new instance of an IEnumerator is created. Next,
it starts a while loop calling MoveNext(), stopping when false is
returned. When it’s true, the Current property returns the new
value.
The generic version of IEnumerable implements IDisposable so
the usingkeyword is used to take care of that.
This is very simple but lets now focus on the nuances…
Count() vs. Any()
It’s good practice to validate arguments and avoid unnecessary
computation, so I’ve found the following very often:
It uses the Count() extension method to check if the enumerable
is empty and exits early. Unfortunately this does way more
computation than most expect…
From the highlighted points on the enumeration interfaces, we
already know that these can only enumerate sequentially
and have no more information on the
collection. Count() implementation looks something like this:
public static int MyCount<T>(this IEnumerable<T>
enumerable)
{
var count = 0;
using(var enumerator = enumerable.GetEnumerator())
while (enumerator.MoveNext())
count++;
return count;
Notice that it has to enumerate all the items, counting the
number of times MoveNext() returns true. As a
consequence, Count() has a complexity ofO(n).
There is a better solution. Any() is an extension method that
returns trueif the enumerable contains at least one
element; otherwise false. Any()implementation looks
something like this:
Notice that it creates an instance of IEnumerator and then
calls MoveNext()only once. There is no loop! Any() has a
complexity of O(1).
You should always use Any() to check if an enumerable is empty:
public void DoSomething<T>(IEnumerable<T> enumerable)
if (!enumerable.Any())
return;
// do something here
}
Usually, you don’t even need to check if it’s empty. The foreach doesn’t enter the loop in that case.
Here’s an implementation of Average() that creates one single instance of IEnumerator and
enumerates the collection only once: