Return an empty IEnumerator
I have an interface that, among other things, implements a "public IEnumerator GetEnumerator()" method, so I can use the interface in a foreach statement.
I implement this interface in several classes and in one of them, I want to return an empty IEnumerator. Right now I do this the following way:
public IEnumerator GetEnumerator()
{
ArrayList arr = new ArrayList();
return arr.GetEnumerator();
}
However I consider this an ugly hack, and I can't help but think that there is a better way of returning an empty IEnumerator. Is there?
This is simple in C# 2:
public IEnumerator GetEnumerator()
{
yield break;
}
You need the yield break
statement to force the compiler to treat it as an iterator block.
This will be less efficient than a "custom" empty iterator, but it's simpler code...
There is an extra function in the framework:
public static class Enumerable
{
public static IEnumerable<TResult> Empty<TResult>();
}
Using this you can write:
var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();
You could implement a dummy class that implements IEnumerator, and return an instance of it:
class DummyEnumerator : IEnumerator
{
public object Current
{
get
{
throw new InvalidOperationException();
}
}
public bool MoveNext()
{
return false;
}
public void Reset()
{
}
}
I was curious and went a bit further. I made a test that checks how efficient the methods are comparing yield break
, Enumerable.Emtpy
and custom class.
You can check it out on dotnetfiddle https://dotnetfiddle.net/p5ZkUN or use the code below.
The result of one of the many dotnetfiddle runs using 190 000 iterations was:
Yield break: 00:00:00.0012208
Enumerable.Empty(): 00:00:00.0007815
EmptyEnumerator instance: 00:00:00.0010226
using System;
using System.Diagnostics;
using System.Collections;
using System.Linq;
public class Program
{
private const int Iterations = 190000;
public static void Main()
{
var sw = new Stopwatch();
IEnumerator enumerator1 = YieldBreak();
sw.Start();
for (int i = 0; i < Iterations; i++)
{
while(enumerator1.MoveNext())
{
throw new InvalidOperationException("Should not occur");
}
}
sw.Stop();
Console.WriteLine("Yield break: {0}", sw.Elapsed);
GC.Collect();
IEnumerator enumerator2 = Enumerable.Empty<object>().GetEnumerator();
sw.Restart();
for (int i = 0; i < Iterations; i++)
{
while(enumerator2.MoveNext())
{
throw new InvalidOperationException("Should not occur");
}
}
sw.Stop();
Console.WriteLine("Enumerable.Empty<T>(): {0}", sw.Elapsed);
GC.Collect();
var enumerator3 = new EmptyEnumerator();
sw.Restart();
for (int i = 0; i < Iterations; i++)
{
while(enumerator3.MoveNext())
{
throw new InvalidOperationException("Should not occur");
}
}
sw.Stop();
Console.WriteLine("EmptyEnumerator instance: {0}", sw.Elapsed);
}
public static IEnumerator YieldBreak()
{
yield break;
}
private class EmptyEnumerator : IEnumerator
{
//public static readonly EmptyEnumerator Instance = new EmptyEnumerator();
public bool MoveNext()
{
return false;
}
public void Reset()
{
}
public object Current { get { return null; } }
}
}