IEnumerable and IEnumerator

Related Article

IEnumerator Interface

Elements in a collection are enumerated in a forward manner only.

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

MoveNext – advances the current element to the next position or returns false if there are no more elements in the collection.

Current – returns the element at the current position.

Reset – moves back to the start of the collection.

Collections do not implement enumerators, instead they provide enumerators via the interface IEnumerable.

public interface IEnumerable
{
	IEnumerable GetEnumerator();
}

IEnumerable

Provides flexibility in that the iteration logic can be farmed off to another class.

Several consumers can enumerate the collection at once without interfering with each other.

IEnumerable can be thought of as IEnumeratorProvider.

string s = "Hello";

// Because string implements IEnumerable, we can call GetEnumerator():
IEnumerator rator = s.GetEnumerator();

while (rator.MoveNext())
{
	char c = (char) rator.Current;
	Console.Write (c + ".");
}

Console.WriteLine();

// Equivalent to:

foreach (char c in s)
	Console.Write (c + ".");

IEnumerable<T> and IEnumerator<T>

IEnumerator and IEnumerable are nearly always implemented in conjunction with their extended generic versions.

public interface IEnumerator : IEnumerator, IDisposable
{
	T Current { get; }
}

public interface IEnumerable : IEnumerable
{
	IEnumerator GetEnumerator();
}

By defining typed version of Current and GetEnumerator, these interfaces strengthen static type safety and avoid the boxing overhead.

Arrays automatically implement IEnumerable<T>.

IEnumerable<T> and IDisposable

IEnumerator<T> inherits from IDisposable.

This allows enumerators to hold references to resources such as database connections to ensure that those resources are released when enumeration is complete.

foreach (var element in somethingEnumerable) { ... }

translates into:

using ( var rator = somethingEnumerable.GetEnumerator())
	while (rator.MoveNext())
	{
		var element = rator.Current;
		...
	}

The using block ensures disposal.

IEnumerable s = "Hello";

using (var rator = s.GetEnumerator())
	while (rator.MoveNext())
	{
		char c = (char) rator.Current;
		Console.Write (c + ".");
	}

Non Generic Interface

void Main()
{
   Count("the quick brown fox".Split());
}

public static int Count(IEnumerable e)
{
   int count = 0;
   foreach (object element in e)
   {
      var subCollection = element as IEnumerable;
      if (subCollection != null)
          count += Count (subCollection);
      else
          count++;
   }
   return count;
}

IEnumerable or IEnumerable<T> can be implemented for one of the reasons:

  • To support the foreach statement.
  • To inter-operate with anything expecting a standard collections.
  • To meet the requirements of a more sophisticated collection interface.
  • To support collection initializers.

To implement IEnumerable/IEnumerable<T>:

  • If class is wrapping another collection, return the wrapped collection’s enumerator
  • Using the iterator yield return
  • By instantiating your own implementation

Using Iterator

void Main()
{
	foreach (int element in new MyCollection())
		Console.WriteLine (element);
}

public class MyCollection : IEnumerable
{
	int[] data = { 1, 2, 3 };
	
	public IEnumerator GetEnumerator()
	{
		foreach (int i in data)
			yield return i;
	}
}

Using Iterator using Generic

void Main()
{
	foreach (int element in new MyGenCollection())
		Console.WriteLine (element);
}

public class MyGenCollection : IEnumerable<int>
{
	int[] data = { 1, 2, 3 };

	public IEnumerator GetEnumerator()
	{
		foreach (int i in data)
			yield return i;
	}

	IEnumerator IEnumerable.GetEnumerator()     // Explicit implementation
	{                                           // keeps it hidden.
		return GetEnumerator();
	}
}

Using Iterator Method

The yield statement allows for an easier variation. Instead of writing a class the iteration logic can be moved into a method returning a generic IEnumerable<T> and let the compiler take care of the rest.

void Main()
{
	foreach (int i in Test.GetSomeIntegers())
		Console.WriteLine (i);
}

public class Test
{
	public static IEnumerable  GetSomeIntegers()
	{
		yield return 1;
		yield return 2;
		yield return 3;
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *