The Iterator pattern. Implementation in C#
Before studying this topic, it is recommended that you familiarize yourself with the topic:
Contents
- 1. The structure of the Iterator pattern in C# codes. Picture
- 2. Programming the components that define the structure of the Iterator pattern
- 3. Client code. Demonstration of the pattern
- Related topics
Search other websites:
1. The structure of the Iterator pattern in C# codes. Picture
Information about the Iterator pattern and how to implement it can be found here. Figure 1 shows the structure of the Iterator pattern with C# code snippets. This structure can be used for any number of containers (polymorphic container) and any number of iterators (polymorphic iterator).
In order to add your own specific container, you need to implement the IAggregate interface in the class. This interface declares a single method, CreateIterator(). This method returns an iterator for the container.
Figure 1. The structure of Iterator pattern
⇑
2. Programming the components that define the structure of the Iterator pattern
To implement the Iterator pattern, you need to develop the following interfaces and classes:
- an IAggregate interface containing declarations for the CreateIterator() method;
- the IIterator interface, which contains basic methods for getting an iterator and moving around a container;
- one or more specific iterators. Here iterators can be declared that perform various traversal strategies. All iterators implement the IIterator interface;
- one or more classes that implement the IAggregate interface.
⇑
2.1. Interfase IAggregate<T> of the iterator
First of all, the container interface IAggregate<T> is declared, containing the declarations of the following methods:
- CreateIterator() – returns an iterator for the container;
- Count() – returns the number of items in the container;
- GetItem() – returns a container item by its index.
According to C# syntax, all of these methods must be implemented in classes that implement the IAggregate<T> interface.
After entering the interface, the program listing is as follows:
using System; namespace ConsoleApp17 { // Implementation of Iterator pattern in C#. // Example for a console application // 1. Declare container interface supporting generic type T interface IAggregate<T> { // Return iterator to a container IIterator<T> CreateIterator(); // The number of elements of the container long Count(); // Return an element by its index T GetItem(long index); } class Program { static void Main(string[] args) { } } }
The interface serves as a glue between the subclasses, which is what the IIterator<T> interface implements.
⇑
2.2. The interface IIterator<T> of iterator
To provide the ability to implement various kinds of iterators, you need to declare the iterator interface IIterator<T>. This interface will contain common methods for all iterator classes that will implement it.
In our case, the following method declarations are introduced into the IIterator<T> interface:
- First() – go to the first item of the container;
- Next() – move the cursor to the next item of the container;
- IsDone() – check if the list is end;
- CurrentItem() – get the current element at the cursor.
Optionally, you can extend the list of methods for inherited iterators. For example, add the Previous() method to navigate to the previous item in the container.
The abbreviated program code after entering the interface is as follows.
using System; namespace ConsoleApp17 { // Implementation the Iterator pattern in C#. // Example for console application // 1. Declare container interface supporting generic T type interface IAggregate<T> { ... } // 2. Declare an iterator interface for type T interface IIterator<T> { // Move to the first element of the container void First(); // Move to the next element of the container void Next(); // Get the current item T CurrentItem(); // Checking if the cursor is pointing to the end of the container bool IsDone(); } ... }
⇑
2.3. Concrete iterator ConcreteIterator<T>
This step creates one or more classes that implement the IIterator<T> interface. In our case, we create one class named ConcreteIterator1<T>. This class implements a forward iterator.
The following program elements are implemented in a specific iterator:
- the internal field aggregate is a reference to the container for which this iterator is being created;
- the internal field current is a value (position) that points to one of the container elements. This is a cursor;
- a constructor that takes an aggregate object as a parameter;
- the First(), Next(), IsDone() and CurrentItem() methods, which override the IIterator<T> interface methods.
using System; namespace ConsoleApp17 { // Implementation of Iterator pattern in C#. // Example for console application // 1. Declare container interface supporting generic T type interface IAggregate<T> { ... } // 2. Declare an iterator interface for type T interface IIterator<T> { ... } // 3. Concrete iterator class ConcreteIterator1<T> : IIterator<T> { // internal data private IAggregate<T> aggregate; // A reference to aggregate private long current; // current position // Constructor taking an aggregate object public ConcreteIterator1(IAggregate<T> obj) { aggregate = obj; current = 0; } // Move cursor to the beginning of the list virtual public void First() { current = 0; } // Move the cursor to the next item in the list virtual public void Next() { current++; } // Check whether the end of the list virtual public bool IsDone() { return current >= aggregate.Count(); } // Return the current list item virtual public T CurrentItem() { if (!IsDone()) return aggregate.GetItem(current); else { throw new NotImplementedException("Error"); } } } ... }
Using the above code sample, you can add other kinds of iterators with arbitrary names. A prerequisite is the implementation (inheritance) of the IIterator<T> interface. If you need to add your own iterator, then the following steps are performed:
- choose the name of the iterator and declare the class of this iterator (for example, ConcreteIterator2<T> or something else). Класс должен реализовывать интерфейс IIterator<T>;
- implement internal data and one or more constructors;
- implement the methods First(), Next(), IsDone(), CurrentItem() declared in the IIterator<T> interface;
- optionally declare additional methods.
One type of iterator is a reverse iterator, which loops through the container from end to beginning. In our case, if it were necessary to implement a reverse iterator (it looks through the elements of the container from end to beginning), then some methods should have a different interpretation:
- the First() method would implement a jump to the last element of the container;
- the Next() method would move the cursor to the previous element in the container.
⇑
2.4. The ConcreteAggregate<T> class
The IAggregate<T> interface declared in clause 2.1 is common to all classes that implement a specific aggregate (container). Thanks to polymorphism, any number of specific aggregates can be implemented in a program. As you know, in each container class, data is represented as a set of elements (list, array, tree, etc.).
If a class of a specific container (aggregate) is created, then a number of requirements are imposed on it:
- the class must implement (inherit) the IAggregate<T> interface;
- the class must contain the minimum functionality for operating with a set of elements;
- the class must implement methods of the IAggregate<T> interface. The required method is the CreateIterator() method.
In our case, the ConcreteAggregate1<T> class is declared, which implements the IAggregate<T> interface. The class contains the following main components:
- internal array field array of generic type T. Any other data structure can be used instead of array: list, tree, set, and the like;
- two constructors;
- Append() method – adds a new item to the end of the array;
- Remove() method – removes an element from an array at a specified position;
- method Print() – displays the array;
- methods declared in the IAggregate<T> interface: CreateIterator(), Count(), GetItem().
The abbreviated program fragment with the code of the ConcreteAggregate1<T> class looks like.
using System; namespace ConsoleApp17 { // Implementation of Iterator pattern in C#. // Example for a console application // 1. Declare container interface supporting generic T type interface IAggregate<T> { ... } // 2. Declare an iterator interface for type T interface IIterator<T> { ... } // 3. Concrete iterator class ConcreteIterator1<T> : IIterator<T> { ... } // 4. Concrete container for items of type T class ConcreteAggregate1<T>:IAggregate<T> { // Internal fields private T[] array; // dynamic array of items of T type // Constructors // Constructor that receives an external array public ConcreteAggregate1(T[] _array) { array = _array; } // Constructor creating an empty array public ConcreteAggregate1() { array = null; } // The method that adds the element to the end of the container public void Append(T item) { T[] array2 = array; array = new T[array2.Length + 1]; array2.CopyTo(array, 0); array[array.Length - 1] = item; } // Removes the element from the container at position index public void Remove(long index) { T[] array2 = array; array = new T[array2.Length - 1]; for (long i = 0; i < index; i++) array[i] = array2[i]; for (long i = index + 1; i < array2.Length; i++) array[i - 1] = array2[i]; } // Display the contents of the container public void Print(string text) { Console.WriteLine(text + ":"); for (int i = 0; i < array.Length; i++) Console.Write(array[i] + " "); Console.WriteLine(); } // Implementation of methods of IAggregate<T> interface // Return iterator public IIterator<T> CreateIterator() { return new ConcreteIterator1<T>(this); } // Number of items public long Count() { return array.Length; } // Return the item public T GetItem(long index) { if ((index >= 0) && (index < array.Length)) return array[index]; else throw new NotImplementedException("Error. Bad index"); } } }
⇑
3. Client code. Demonstration of the pattern
Below is the client code that demonstrates how to get an iterator and how to use it.
using System; namespace ConsoleApp17 { // Implementation of Iterator pattern in C#. // Example for console application // 1. Declare container interface supporting generic T type interface IAggregate<T> { ... } // 2. Declare an iterator interface for type T interface IIterator<T> { ... } // 3. Concrete iterator class ConcreteIterator1<T> : IIterator<T> { ... } // 4. Concrete container for items of type T class ConcreteAggregate1<T>:IAggregate<T> { ... } class Program { static void Main(string[] args) { // Testing the Iterator pattern - client code // 1. Create an array of numbers double[] A = { 2.3, 3.2, 4.4, 5.1, 8.2 }; // 2. Create container instance based on array A ConcreteAggregate1<double> ag1 = new ConcreteAggregate1<double>(A); ag1.Print("ag1"); // display the container // 3. Create iterator for ag1 instance ConcreteIterator1<double> it1 = (ConcreteIterator1<double>)ag1.CreateIterator(); // 4. Demonstration of the iterator operation Console.WriteLine("----------------------"); Console.WriteLine("Access using iterator:"); it1.First(); while (!it1.IsDone()) { Console.Write("{0} ", (double)it1.CurrentItem()); it1.Next(); } Console.WriteLine(); Console.ReadKey(); } } }
After starting, the program produces the following result
ag1: 2.3 3.2 4.4 5.1 8.2 ---------------------- Access using iterator: 2.3 3.2 4.4 5.1 8.2
⇑
Related topics
- General information. Methods of implementation. Structural scheme. Example on C++
- The Iterator pattern. Features of implementation in C++ for polymorphic container and polymorphic iterator
- External and internal iterator. Implementation in C++
- The Iterator pattern. Java implementation with support for polymorphic container and polymorphic iterator
⇑