IEnumerable and IEnumerator

foreach is pretty convenient in C#. It requires the iterated collection implemented IEnumerable or IEnumerable<T>.

public interface IEnumerable
{
	IEnumerator GetEnumerator();
}

public interface IEnumerable<out T> : IEnumerable
{
	IEnumerator<T> GetEnumerator();
}

You can see that there’s only one method in this interface which returns an enumerator. The generic type T has an out modifier.

public interface IEnumerator
{
	//
	// Summary:
	//     Gets the element in the collection at the current position of the enumerator.
	//
	// Returns:
	//     The element in the collection at the current position of the enumerator.
	object Current { get; }

	//
	// Summary:
	//     Advances the enumerator to the next element of the collection.
	//
	// Returns:
	//     true if the enumerator was successfully advanced to the next element; false if
	//     the enumerator has passed the end of the collection.
	//
	// Exceptions:
	//   T:System.InvalidOperationException:
	//     The collection was modified after the enumerator was created.
	bool MoveNext();
	//
	// Summary:
	//     Sets the enumerator to its initial position, which is before the first element
	//     in the collection.
	//
	// Exceptions:
	//   T:System.InvalidOperationException:
	//     The collection was modified after the enumerator was created.
	void Reset();
}

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

Basically, the IEnumerator has three property and methods: Current, MoveNext and Reset. Note that it should raise InvalidOperationException if the collection is modified after the enumerator is created.

Here’s my sample:

public class Card
{
	public Card(Suit suit, Ranking ranking)
	{
		Suit = suit;
		Ranking = ranking;
	}

	public Suit Suit { get; }
	public Ranking Ranking { get; }
}

public class Deck : IEnumerable<Card>
{
	private const int INIT_SIZE = 10;
	private const float EXTEND_FAC = 1.5f;
	private Card[] _cards;
	private int _size;
	private int _count;

	public int Count => _count;

	public Deck()
	{
		_cards = new Card[INIT_SIZE];
		_size = INIT_SIZE;
		_count = 0;
	}

	public void Add(Card card)
	{
		if (_count == _size)
		{
			var newSize = (int)(_size * EXTEND_FAC);
			var newArray = new Card[newSize];
			_size = newSize;
			for (int i = 0; i < _count; i++)
			{
				newArray[i] = _cards[i];
			}
			_cards = newArray;
		}
		_cards[_count++] = card;
	}

	public IEnumerator<Card> GetEnumerator() => new CardEnumerator(_cards, _count);

	IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}

public class CardEnumerator : IEnumerator<Card>
{
	private Card[] _cards;
	private int _count;
	private int _currentIndex = -1;

	public CardEnumerator(Card[] cards, int count)
	{
		_cards = cards;
		_count = count;
	}

	public Card Current
	{
		get
		{
			try
			{
				return _cards[_currentIndex];
			}
			catch (IndexOutOfRangeException)
			{
				throw new InvalidOperationException();
			}
		}
	}

	object IEnumerator.Current => this.Current;

	public void Dispose()
	{
	}

	public bool MoveNext()
	{
		if (_currentIndex < _count - 1)
		{
			_currentIndex++;
			return true;
		}
		else
		{
			return false;
		}
	}

	public void Reset()
	{
		_currentIndex = -1;
	}
}

Leave a comment