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;
}
}