Brendan Enrick

Daily Software Development

Implementing IEnumerable and IEnumerator

Working with a foreach loop is the primary reason to implement the IEnumerable and IEnumerator interfaces. You’ll want one of each of these to work with the loop.

I am going to do an example DateRange class which will implement IEnumerable<DateTime> and will allow us to iterate through a non-existent collection of DateTime objects.

Note: I am aware of the fact that I could achieve the same result with a for loop. I find the foreach loop more readable.

First we need to create a basic DateRange class. A range can be defined as a StartDate and an EndDate, so I’ll start there.

public class DateRange
{
    public DateRange(DateTime startDate, DateTime endDate)
    {
        StartDate = startDate;
        EndDate = endDate;
    }
 
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

So this DateRange could be useful on its own, but we want to be able to iterate this collection using a foreach. So to start we need to implement the IEnumerable<DateTime> interface.

public class DateRange : IEnumerable<DateTime>
{
    public DateRange(DateTime startDate, DateTime endDate)
    {
        StartDate = startDate;
        EndDate = endDate;
    }
 
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
 
    public IEnumerator<DateTime> GetEnumerator()
    {
        return new DateRangeEnumerator(this);
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

 

Notice here that we now need to get the IEnumerator<DateTime> object in the GetEnumerator() method. I jumped the gun a bit and I’ve called a class that doesn’t exist yet. I’ll make another class and implement the required methods for the IEnumerator interface.

public class DateRangeEnumerator : IEnumerator<DateTime>
{
    private int _index = -1;
    private readonly DateRange _dateRange;
 
    public DateRangeEnumerator(DateRange dateRange)
    {
        _dateRange = dateRange;
    }
 
    public void Dispose()
    {
    }
 
    public bool MoveNext()
    {
        _index++;
        if (_index > (_dateRange.EndDate - _dateRange.StartDate).Days)
            return false;
        return true;
    }
 
    public void Reset()
    {
        _index = -1;
    }
 
    public DateTime Current
    {
        get { return _dateRange.StartDate.AddDays(_index); }
    }
 
    object IEnumerator.Current
    {
        get { return Current; }
    }
}

 

These are the handful of methods we implement for the IEnumerator<DateTime> interface. These are all about moving to the next object and getting the current object. Resetting and Disposal of the object are less important, so make sure you read MoveNext and Current.

Keep in mind here that I could have used a collection for this, but I didn’t because I don’t need one. The calculation to get the items was easy enough.

var dateRange = new DateRange(DateTime.Today.AddDays(-6), DateTime.Today);
foreach (DateTime date in dateRange)
{
    Console.WriteLine(date.ToShortDateString());
}

Output:

10/20/2009
10/21/2009
10/22/2009
10/23/2009
10/24/2009
10/25/2009
10/26/2009

Comments (1) -

  • Yair Nevet

    4/23/2011 3:12:20 AM | Reply

    Dear Brendan,

    I really like your post.
    It makes me understand the concept of the all issue.

    Thank you!

    Yair

Loading