Return an empty IEnumerator
Asked Answered
B

9

72

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?

Barley answered 11/11, 2009 at 10:35 Comment(0)
S
102

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

Silvas answered 11/11, 2009 at 10:43 Comment(2)
Note that this solution does not work in all cases - you cannot combine yield break with returning some other enumerator if (some case) { yield break; } else { return SomeStaticMethodThatGivesEnumerable();} // does not work. In that case, use the Empty option from the other solution. Thank you everyone involved.Inexpugnable
@PeterKay: But the question wasn't how to return an empty enumerator on a case where there's additional logic - it's how to return an empty enumerator when that's all the OP needs to do. If the question had been different, my answer would have been different too.Silvas
N
95

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();
Ngocnguyen answered 17/3, 2010 at 11:24 Comment(1)
To get an IEnumerator<T> instead of an IEnumerable<T>: Enumerable.Empty<Whatever>().GetEnumerator();Batish
U
13

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()
    {
    }
}
Unbroken answered 11/11, 2009 at 10:42 Comment(0)
L
9

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; } }
    }
}
Leuctra answered 28/11, 2016 at 17:40 Comment(2)
What about creating an empty ArrayList/List and return its enumerator as the OP showed (ugly hack). Is it slower than the methods you measured here?Drawers
You need to move the call to get the enumerator on the first two tests out of the loop as well, otherwise it's not a useful comparison.Scar
B
6

The way I use is to use the enumerator of an empty array:

public IEnumerator GetEnumerator() {
    return new object[0].GetEnumerator();
}

It can also be used for generic IEnumerator or IEnumerable (use an array of the appropriate type)

Blooming answered 11/11, 2009 at 10:53 Comment(0)
F
1

You can implement IEnumerator interface and IEnumerable, and return false from MoveNext function of IEnumerable interfase

private class EmptyEnumerator : IEnumerator
{


    public EmptyEnumerator()
    {
    }

    #region IEnumerator Members

    public void Reset() { }

    public object Current
    {
        get
        {
            throw new InvalidOperationException();
        }
    }
    public bool MoveNext()
    { return false; }
}


public class EmptyEnumerable : IEnumerable
{

    public IEnumerator GetEnumerator()
    {
        return new EmptyEnumerator();
    }
}
Flunkey answered 11/11, 2009 at 10:39 Comment(1)
According to MSDN documentation, Current should throw an exception in this case (msdn.microsoft.com/en-us/library/…)Unbroken
J
1

I wrote it like this:

public IEnumerator<T> GetEnumerator()
{
    return this.source?.GetEnumerator() ??
            Enumerable.Empty<T>().GetEnumerator();
}
Joella answered 27/11, 2015 at 12:43 Comment(0)
S
0

You can make a NullEnumerator which implements the IEnumerator interface. You can just pass an instance off the NullEnumerator.

here is an example of an EmptyEnumerator

Squally answered 11/11, 2009 at 10:38 Comment(0)
H
0

Found this question looking for the simplest way to get an empty enumerator. After seeing the answer comparing performance I decided to use the empty enumerator class solution, but mine is more compact than the other examples, and is a generic type, and also provides a default instance so you don't have to create new instances all the time, which should even further improve performance.

class EmptyEnumerator<T> : IEnumerator<T>
{
   public readonly static EmptyEnumerator<T> value = new EmptyEnumerator<T>();
   public T Current => throw new InvalidOperationException();
   object IEnumerator.Current => throw new InvalidOperationException();
   public void Dispose() { }
   public bool MoveNext() => false;
   public void Reset() { }
}
Habitancy answered 3/12, 2017 at 17:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.