Click here to Skip to main content
15,890,438 members
Articles / Programming Languages / C#
Tip/Trick

Define your own collection class that supports the Iterator pattern

Rate me:
Please Sign up or sign in to vote.
2.00/5 (1 vote)
2 Apr 2012CPOL2 min read 11.9K   2   6
In this article we are discussing how to define Iterator classes for our own classes.

Introduction

In my previous article How to use the IEnumerable/IEnumerator interface, I discussed how to use these interfaces with collection classes. Today we will see how these interfaces work. For that we will develop our own classes that implement these interfaces.

Objective

Define the Enumerable and Enumerator object for the user defines data type (i.e., class).

Using the code

First of all, let’s define a class for which we are going to develop the Iterator classes. Here is the class:

C#
class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return string.Join(" | ", FirstName, LastName, Age);
    }
}

As you can see, here we define the Employee class. This class has three properties – FirstName, LastName, and Age and an override method ToString which returns a string representation of the class. Now we’ll define the Enumerable class for Employee called EmployeeCollection which implements the System.Collection.IEnumerable interface. This class contains an Array of the Employee class as its data member. This member is initialized in the constructor as shown below:

C#
public EmployeeCollection(Employee[] listEmployee)
{
    employess = new Employee[listEmployee.Length];
    Array.Copy(listEmployee, employess, listEmployee.Length);
}

In this example, we are implementing the IEnumerable interface explicitly. The reason for doing this is while using the GetEnumerator method, I want the user to only get my version of the GetEnumerator method. Let us see how to do this:

C#
// This method implemented explicitly, that's why it's
// not visible to user. So user will always use second 
// version of this method.
IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator() as IEnumerator;
}

// User can use this version only
public EmployeeEnumerator GetEnumerator()
{
    return new EmployeeEnumerator(employess);
}

This way we can hide IEnumerable’s method implementation with our own version. Till now we had developed an Enumerable class for our own class. Now we are going to define the Enumerator class for our entity. Below we’ve the code for the Enumerator class.

C#
class EmployeeEnumerator : IEnumerator
{
    Employee[] employess;
    int position = -1;

    public EmployeeEnumerator(Employee[] lstEmployee)
    {
        employess = lstEmployee;
    }

    public Employee Current
    {
        get
        {
            try
            {
                return employess[position];
            }
            catch (IndexOutOfRangeException) { throw new InvalidCastException(); }
        }
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public bool MoveNext()
    {
        position++;
        return position < employess.Length;
    }

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

You can see here that  we are implementing methods of the IEnumerator interface explicitly. I think this class is self describable. Now we are moving into how to use this concept. Here is some code that uses these classes.

C#
static class Enumerator
{
    [STAThread]
    static void Main(string[] args)
    {
        Employee[] list = new Employee[]{
            new Employee() { FirstName="Himanshu", LastName="Manjarawala", Age=30},
            new Employee(){ FirstName="Hetal", LastName="Sangani", Age=26},
            new Employee(){ FirstName="Viral", LastName="Sangani", Age=32},
            new Employee(){ FirstName="Rajesh", LastName="Patel", Age=29},
            new Employee(){ FirstName="Nehal", LastName="Thakkar", Age=30}
        };

        var enumerable = new EmployeeCollection(list);
        var enumerator = enumerable.GetEnumerator();

        while (enumerator.MoveNext())
        {
            Employee e = enumerator.Current;
            Console.WriteLine(e.ToString());
        }
    }
}

Here, we’ve defined an Array of our own Employee class. You can define a List<> as well. As we know, both are of the System.Collection type, so they internally support Enumerable methods. We instantiate our own EmployeeCollection class and then from there we’ll get our Enumerator object for the Employee class using the GetEnumerator() method and from that object we can iterate over our array members. Hope you like this concept of the Iterator pattern.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader Automation Anywhere Inc.
India India
I am Himanshu Manjarawala, Garduate in Computer Science and MCA From Veer Narmad South Gujarat University, Surat Gijarat India. Currently working as Sr. Software Developer in Automation Anywhere Softwares Pvt. Ltd. Vadodara, Gujarat

Comments and Discussions

 
QuestionWhy not inherit from List&lt;T&gt;? Pin
Bernhard Hiller2-Apr-12 21:35
Bernhard Hiller2-Apr-12 21:35 
AnswerRe: Why not inherit from List<T>? Pin
Andreas Gieriet2-Apr-12 22:32
professionalAndreas Gieriet2-Apr-12 22:32 
QuestionOver-Engineered? Pin
Andreas Gieriet2-Apr-12 12:10
professionalAndreas Gieriet2-Apr-12 12:10 
I have the impression that this tip is over-engineered.
The solution that I know (if you ever are tempted to implement your own iterator), is to simply provide a GetEnumerator() method - no need to implement any interface nor to inherit from any class. And no need to implement your own EmployeeEnumerator class. Keep it simple!

C#
public class EmployeeCollection // no implementation or inheritance needed
{
    private Employee[] _employees;
    public EmployeeCollection(params Employee[] listEmployee)
    {
        _employees = new Employee[listEmployee.Length];
        Array.Copy(listEmployee, _employees, listEmployee.Length);
    }
    public IEnumerator GetEnumerator() { return _employees.GetEnumerator(); }
}


And if you want to have a specific iterator, then you may define a method or a property that returns an IEnumerable or IEnumerable<Employee> (compared to an IEnumerator of the GetEnumerator() method). E.g.

C#
public IEnumerable Reverse { get { return _employees.Reverse(); } }


or employing yield:

C#
public IEnumerable Range(int from, int count)
{
    for (int n = 0; n < count && from+n < _employees.Length; n++)
    {
        yield return _employees[from + n];
    }
}


Usage:
C#
static void Main(string[] args)
{
    var coll = new EmployeeCollection
    ( new Employee { FirstName="Himanshu", LastName="Manjarawala", Age=30}
    , new Employee { FirstName="Hetal", LastName="Sangani", Age=26}
    , new Employee { FirstName="Viral", LastName="Sangani", Age=32}
    , new Employee { FirstName="Rajesh", LastName="Patel", Age=29}
    , new Employee { FirstName="Nehal", LastName="Thakkar", Age=30}
    );
    Console.WriteLine("### GetEnumerator() ###");
    foreach (Employee item in coll) Console.WriteLine(item);
    Console.WriteLine("### Reverse ###");
    foreach (Employee item in coll.Reverse) Console.WriteLine(item);
    Console.WriteLine("### Range(...) ###");
    foreach (Employee item in coll.Range(1,3)) Console.WriteLine(item);
}


Output:
### GetEnumerator() ###
Himanshu | Manjarawala | 30
Hetal | Sangani | 26
Viral | Sangani | 32
Rajesh | Patel | 29
Nehal | Thakkar | 30
### Reverse ###
Nehal | Thakkar | 30
Rajesh | Patel | 29
Viral | Sangani | 32
Hetal | Sangani | 26
Himanshu | Manjarawala | 30
### Range(...) ###
Hetal | Sangani | 26
Viral | Sangani | 32
Rajesh | Patel | 29


PS: You may always define your methods with the generic return type (which I prefer). E.g.
C#
public IEnumerator<Employee> GetEnumerator()
{ return _employees.Cast<Employee>().GetEnumerator(); }
public IEnumerable<Employee> Reverse { get { return _employees.Reverse(); } }
public IEnumerable<Employee> Range(int from, int count)
{
    for (int n = 0; n < count && from+n < _employees.Length; n++)
    {
        yield return _employees[from + n];
    }
}


modified 2-Apr-12 18:36pm.

AnswerRe: Over-Engineered? Pin
Himanshu Manjarawala2-Apr-12 17:33
Himanshu Manjarawala2-Apr-12 17:33 
GeneralRe: Over-Engineered? Pin
Andreas Gieriet2-Apr-12 20:38
professionalAndreas Gieriet2-Apr-12 20:38 
GeneralRe: Over-Engineered? Pin
Andreas Gieriet3-Apr-12 23:35
professionalAndreas Gieriet3-Apr-12 23:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.