This is part of a series of articles:
- Enumeration in .NET
- Enumeration in .NET II —
- Enumeration in .NET III —
- Enumeration in .NET IV — Finding an item
- Enumeration in .NET V —
Enumerable.Empty<T>() is a static method in the
System.Linq namespace and returns the simplest implementation of
IEnumerable<T>. I think it’s interesting to see how it really works to better understand enumeration in .NET.
In a previous article I wrote that it returns
an IEnumerable implementation that generates IEnumerator implementations where the method MoveNext() always returns false.
This is how it looks like in code:
NOTE: This is not the implementation found in
System.Linq. The implementation up to version Core 2.1 is very complex and inefficient. The current implementation found in the corefx repository is much closer to the one in this article. I wrote an article comparing these two and many more forms of implementing
Enumerable.Empty<T>() is simply a static method in a static class. It returns a new instance of an inner struct
EmptyEnumerable<T> that implements
EmptyEnumerable<T> is a struct for better performance. Using the C# 7.2 syntax, it’s marked as readonly as
IEnumerables should be immutable and the keyword may improve performance. It implements both the generic and the non-generic versions of
GetEnumerator(). The non-generic is explicitly implemented. Both return a new instance of the inner struct
For cases other than
Empty, the enumerable instance can be passed as an in argument of the enumerator constructor so that the enumerator can access the enumerable private fields.
Enumerator is also a struct and implements
IEnumerator<T>. It inherits the T from its outer struct. It implements
MoveNext() by always returning false. The generic and non-generic
Current property return the default value of
Reset() can be an empty method in this case but I’m throwing an exception to show you what other
IEnumerators do when they don’t support
Dispose() is empty as it doesn’t need to release any resources.
It’s marked as readonly for this specific case but other
IEnumeratorsare not readonly as they change inner state when
As a curiosity, you can also implement
Enumerable.Empty<T>() in a couple lines. The compiler does all the work for you…
Check the generated code at SharpLab.io.
Performance (added 2018/9/20)
The source code shown above is an easier to understand and also more performant version to the one found currently in LINQ. A recent merge request in the dotnet/corefx repository changes it to something very close to this one. The difference is that they use a singleton of a class that implements both
IEnumerator (only possible because it’s immutable).
I was curious about the performance difference between the various implementations so I run some benchmarks and wrote an article on the results: Performance of value-type vs reference-type enumerators