Iterator パターンとは要素の集合体(配列やリストなど、複数の物が含まれている物)の中の要素を、一つ一つ取り出しながら、集合体を走査するパターンです。
このパターンを適用することにより
C# には コレクション系オブジェクトの中身を順番に処理していくための構文として foreach 文があります。
既成の言語、たとえば Java での Iterator の基本的な使用法は以下のような感じです。
Iterator iterator = someAggreegate.iterator();
iterator.First();
while ( !iterator.IsDone())
{
object element = iterator.CurrentItem();
System.out.println (" value:" + element.someValue);
iterator.Next();
}
Iterator iterator = someAggreegate.iterator();
for ( iterator.First(); !iterator.IsDone(); iterator.Next())
{
object element = iterator.CurrentItem();
System.out.println ( " value:" + element.someValue);
}
foreach ( Object o in someCollection)
{
System.Console.WriteLine ( " value:{0}", o.ToString());
}
では、foreach 文で回せるコレクションを作ってみましょう。.NET のライブラリには IEnumerable インターフェイス (Aggregate)と IEnumerator インターフェイス (Iterator)が用意されています。
GoF | .NET |
---|---|
Aggregate クラス | System.Collections.IEnumerable インターフェイス |
Iterator クラス | System.Collections.IEnumerator インターフェイス |
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
using System;
using System.Collections;
// プログラムのエントリーポイントを提供するクラスです。
public class TestApp
{
public static void Main()
{
TestAggregate testAggregate = new TestAggregate();
Console.WriteLine ("*** foreach を開始します。 ***");
foreach (TestElement elem in testAggregate)
{
Console.Write ("{0} ", elem.nNumber);
}
Console.WriteLine ("*** foreach を終了します。 ***");
}
}
public class TestElement
{
private int m_nNumber;
public int nNumber
{
get
{
return m_nNumber;
}
}
public TestElement ( int nNumber)
{
m_nNumber = nNumber;
}
}
// コレクションクラスです。
public class TestAggregate : IEnumerable
{
public TestElement[] testElement;
public TestAggregate()
{
int nCnt;
testElement = new TestElement[3];
for ( nCnt = 0; nCnt < testElement.Length; nCnt++)
{
testElement[nCnt] = new TestElement ( nCnt);
}
}
public IEnumerator GetEnumerator()
{
return new TestEnumerator ( this);
}
// Iterator の役割をするクラスです。( コレクションの内部クラスになっています。)
private class TestEnumerator : IEnumerator
{
private TestAggregate m_parent;
private int m_nCnt = 0;
public TestEnumerator ( TestAggregate ta)
{
Console.Write ("Iterator のコンストラクタです。\n");
m_parent = ta;
this.Reset();
Console.Write ("直前の Reset() は Iterator が呼び出しています( foreach 文による呼び出しではありません。)\n");
}
public bool MoveNext()
{
Console.Write ("MoveNext() です。次の要素に移動します。\n");
m_nCnt++;
if ( m_nCnt < m_parent.testElement.Length)
{
// まだ残りの要素がある場合は true を返す
return true;
}
else
{
// 全ての要素を列挙し終わったら false を返す
return false;
}
}
public object Current
{
get
{
Console.Write ("プロパティ Current の取得です。現在の要素を返します\n");
return m_parent.testElement[m_nCnt];
}
}
public void Reset()
{
Console.Write ("Reset() です。初期化します\n");
m_nCnt = -1;
}
}
}
C# で Iterator パターンを適用するときは IEnumerable と IEnumerator を実装する形にすると foreach 文でのお気楽操作が可能になります。