return type of public IEnumerator GetEnumerator()?

3.7k views Asked by At

To implement collection, I have to complete GeEnumerator(). But the return type of this function is IEnumerator. How can it be done ? For example..

class MyList : IEnumerable, IEnumerator
{
    private int[] array;
    int positino = -1;        
    public IEnumerator GetEnumerator()
    {
        for(int i = 0 ; i < array.Length; ++i)
        {
              yield return array[i];
        }
    }
}

array[i] is integer not IEnumerator type. How can this function return integer?

3

There are 3 answers

0
Jon Hanna On BEST ANSWER

How can this function return integer

It doesn't, it yield returns an integer. This means that it can be compiled into a method that returns IEnumerable, IEnumerable<int>, IEnumerator or IEnumerator<int> depending on which your method says it returns.

The yield keyword is a convenience that allows for the creation of an enumerator with the appropriate MoveNext(), Current, and Dispose, and if it's an enumerable then the appropriate GetEnumerator too.

Looking at your method:

public IEnumerator GetEnumerator()
{
  for(int i = 0 ; i < array.Length; ++i)
  {
      yield return array[i];
  }
}

Then this is something that compiles, though to really work it needs more to actually assign something to array.

When we compile it, it has the same result as if we'd written:

private class En : IEnumerator<object>
{
  private object _current;
  private int _state;
  public MyList _this;
  public int _index;
  public bool MoveNext()
  {
    int state = _state;
    switch(state)
    {
      case 0:
        _state = -1;
        _index = 0;
        break;
      case 1:
        _state = -1;
        if(++_index >= _this.array.Length)
          return false;
        break;
      default:
        return false;
    }
    _current = _this.array[_index];
    _state = 1;
    return true;
  }
  public object Current
  {
    get { return _current; }
  }
  public void Reset()
  {
    throw new NotSupportedException();
  }
  public void Dispose()
  {
  }
  object IEnumerator.Current
  {
    get { return _current; }
  }
  public En(int state)
  {
    _state = state;
  }
}
public IEnumerator GetEnumerator()
{
  var en = new En(0);
  en._this = this;
  return en;
}

The main difference is that En and its fields would all have names that aren't valid C# names but are valid .NET names, so it can't clash with any names a programmer used.

This has a bit more to it than it needs. It implements IEnumerator<object> as well as IEnumerator, but then that means the compiler can just have logic for IEnumerable<T> and use object for the T for the non-generic type rather than having separate logic. _state is more complicated than necessary, but if the yield-using method was more complicated than a single loop, then different values for _state would allow it to keep track of which part of that yield-using method it was corresponding with.

In all it's done a good job of implementing IEnumerator. It's a bit larger than if you were to hand-code one (and obviously larger than if you'd just used return array.GetEnumerator();), but not a lot larger, but conversely the yield-using method is a lot shorter and often these methods are simpler. This is even more so in cases where e.g. you have a using block in the yield-using method, which gets turned into the appropriate clean-up in the enumerator's Dispose().

0
Michal Franc On

Use the generics and IEnumerable int

public class MyList : IEnumerable<int>
{
    public MyList()
    {
        array = new [] { 1, 2, 3, 4, 5, 6, 7};
    }

    private int[] array;
    int positino = -1;        
    public IEnumerator<int> GetEnumerator()
    {
        for(int i = 0 ; i < array.Length; ++i)
        {
              yield return array[i];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
0
xanatos On

Note that in this specific case you can "cheat" and not really implement GetEnumerator(), using the array implementation:

public class MyList : IEnumerable<int>
{
    public MyList()
    {
        array = new[] { 1, 2, 3, 4, 5, 6, 7 };
    }

    private int[] array;

    public IEnumerator<int> GetEnumerator()
    {
        return ((IEnumerable<int>)array).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}