Enumeration in .NET III — Enumerable.Empty<T>()

Antão Almada
3 min readJun 28, 2018

--

Summer Rain by aalmada

This is part of a series of articles:

Enumerable.Empty<T>()

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:

public static class Enumerable
{
public static EmptyEnumerable<T> Empty<T>() =>
new EmptyEnumerable<T>();

public readonly struct EmptyEnumerable<T> : IEnumerable<T>
{
public Enumerator GetEnumerator() =>
new Enumerator();

IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
new Enumerator();

IEnumerator IEnumerable.GetEnumerator() =>
new Enumerator();

public readonly struct Enumerator: IEnumerator<T>
{
public bool MoveNext() =>
false;

public T Current =>
default;

object IEnumerator.Current =>
default;

public void Reset() =>
throw new NotSupportedException();

public void Dispose() {}
}
}
}

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 Empty<T>().

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 IEnumerable<T>.

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 Enumerator.

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 T. 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 Reset(). Dispose() is empty as it doesn’t need to release any resources.

It’s marked as readonly for this specific case but other IEnumerators are not readonly as they change inner state when MoveNext() is called.

This is a nice template for implementing other IEnumerable. For a more complex implementation check my other article How to use Span<T> and Memory<T>.

As a curiosity, you can also implement Enumerable.Empty<T>() in a couple lines. The compiler does all the work for you…

public static class Enumerable
{
public static IEnumerable<T> Empty<T>()
{
yield break;
}
}

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 IEnumerable and 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

--

--