Thursday 22 May 2008

IEnumerable, IEnumerators, IEnumerable<string> and yield

If you want to use custom data collection in the foreach loop, you have to make sure that the collection imprements a GetEnumerator() method that returns an IEnumerator interface.
In order to indicate that your list implements the GetEnumerator() method you can implement the IEnumerable interface as follows:


public class MyEnumerableList : IEnumberable
{
public IEnumerator GetEnumerator(){...}

}


Here are the framework definitions for .NET Framework 1

public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current{get;}
bool MoveNext();
void Reset();
}


Here is a custom class that implements an iterator in .Net 1.

public class MyNumbers : IEnumerable
{
string[] _numberNames = { "Eins", "Zwei", "Drei", "Vier" };

IEnumerator IEnumerable.GetEnumerator()
{
return new MyEnumerator(this);
}

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

//Inner class for the iteration
class MyEnumerator : IEnumerator
{
// inner collection
MyNumbers _myNumbers;
// keep the index in mind
int _current;
// constructor
public MyEnumerator(MyNumbers collection)
{
_myNumbers = collection;
_current = -1;
}
void IEnumerator.Reset()
{
_current = -1;
}
bool IEnumerator.MoveNext()
{
_current++;
return (_current < _myNumbers._numberNames.Length);
}
string IEnumerator.Current
{
get
{
if (_current == -1) throw new InvalidOperationException();
return _myNumbers._numberNames[_current];
}
}
object IEnumerator.Current
{
get
{
return ((IEnumerator)this).Current;
}
}
public void Dispose() { }
}
}


In the .NET Framework 2.0 they added a generic interface that inherits from the older one.

public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<T> : IEnumerator,IDisposable
{
T Current{get;}
}


Add now thanks to the new yield statement the compiler generates the inner iterator class itself in order to keep the state.

public class MyNumbers2 : IEnumerable<string>
{
string[] _numberNames = { "Eins", "Zwei", "Drei", "Vier" };
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
for (int i = 0; i < _numberNames.Length; i++)
yield return _numberNames[i];
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<string>)this).GetEnumerator();
}
}

No comments: