Saturday, December 10, 2005

Iterators in C# 2.0

An iterator is a section of code that returns an ordered sequence of values of the same type.

In C# 1.1, the Iterator design pattern is used to shield iterating clients from the actual implementation details of the underlying data structure, enabling the use of the same client-side iteration logic over multiple data structures.

But there are some problems. The first is that if the collection contains value types, obtaining the items requires boxing and unboxing them because IEnumerator.Current returns an Object. This results in potential performance degradation and increased pressure on the managed heap. Even if the collection contains reference types, you still incur the penalty of the down-casting from Object. While unfamiliar to most developers, in C# 1.0 you can actually implement the iterator pattern for each loop without implementing IEnumerator or IEnumerable.

The Microsoft .NET Framework 2.0 solves this problem by defining the generic, type-safe IEnumerable and IEnumerator interfaces.

Besides making use of generics, the new interfaces are slightly different than their predecessors. Unlike IEnumerator, IEnumerator derives from IDisposable and does not have a Reset method.

The second and more difficult problem is implementing the iterator. Although that implementation is straightforward for simple cases, it is challenging with more advanced data structures, such as binary trees.

Using iterators in C# 2.0, you can have the C# compiler generate the implementation of IEnumerator for you. The C# compiler can automatically generate a nested class to maintain the iteration state. You can use iterators on a generic collection or on a type-specific collection. All you need to do is tell the compiler what to yield in each iteration. The following is an example.

public class CityCollection : IEnumerable
{
string[] m_Cities = {"New York","Paris","London"};
public IEnumerator GetEnumerator()
{
for(int i = 0; i << m_Cities.Length; i++)
yield return m_Cities[i];
}
}

Your collection can easily expose multiple iterators, each used to traverse the collection differently. For example, to traverse the CityCollection class in reverse order. You can stop the iteration midstream by using the yield break statement.

The compiler-generated iterator object (and its state) does not persist across foreach loops. This is why IEnumerabledoes not define a Reset method.

Adopted from Joval Lowy's article C# 2.0: Create Elegant Code with Anonymous Methods, Iterators, and Partial Classes.

0 Comments:

Post a Comment

<< Home