C#に元々ある機能で学ぶデザインパターン "Iterator Pattern"


概要

何番煎じか分かりませんが、イテレータパターン(Iterator Pattern)のお話です。
C#においては、言語に組み込まれていると言って良いでしょう。
Iterator パターン @ Wikipediaのクラス図に合わせた形で、C#の機能を説明していきます。

Aggregate -----------------> Iterator
    △                          △
    │                           │
Concrete                     Concrete
Aggregate -----------------> Iterator

IEnumerator<T>

デザインパターンで出てくるIteratorインタフェースは、C#ではIEnumerator<T>に当たります。
『列挙する者』みたいな意味(Enumurate + -or)ですね。

Current,MoveNext()

next(),hasNext()は、C#ではCurrent,MoveNext()のように少し違った形で実装されます。

interface IEnumerator<out T> {
    // 現在の要素を取得する。
    T Current { get; }
    // 次の要素に移動する。次の要素があればtrue,無ければfalseを返す。
    bool MoveNext();
    // その他の定義は省略
}

IEnumerable<T>

デザインパターンで出てくるAggregateインタフェースは、C#ではIEnumerable<T>に当たります。
『列挙可能』みたいな意味(Enumurate + -able)ですね。

GetEnumerator()

iterator()メソッドは、C#ではGetEnumerator()に当たります。

interface IEnumerable<out T> {
    // イテレータを取得
    IEnumerator<T> GetEnumerator();
    // その他の定義は省略
}

IEnumerable<T>を実装した型

ConcreteAggregateに当たるC#の型は無数にあります。
同一の型のインスタンスを複数扱うようなデータ型はほぼこれに当てはまると思って良いでしょう。

  • 配列
  • List<T>
  • Dictionary<TKey,TValue>

IEnumerator<T>を実装した型

IEnumerator<T>を実装した型は、例えばList<int>.Enumerator型などがあります。
ただ、こちらは実装型を意識せずに利用することが普通ですね。

組み込まれている機能

foreach構文

foreach構文では、GetEnumerator()メソッドを持つインスタンスであれば、同一の構文で要素を列挙できます。
これはC#プログラマなら無意識レベルで使っているであろう例です。

foreach (var item in obj) {
    Console.WriteLine(item);
}

↑の構文は↓とほぼ同義になります。

var iterator = obj.GetEnumerator();
while (iterator.MoveNext()) {
    Console.WriteLine(iterator.Current);
}
// 実際には例外処理や後処理などがあるが、ここでは省略

LINQ

LINQ to Objectでは、IEnumerable<T>を『流れるようなインタフェース』として扱うことで、様々な標準クエリを利用可能にしています。

Enumerable
   .Range(3, 10)
   .Where(i => i % 2 == 1)
   .Select(i => new { A = i, B = i * i / 2, C = i * i / 2 + 1 })
   .ToList()
   .ForEach(Console.WriteLine);

ジェネレータ

C#ではIEnumerable<T>を利用したイテレータを生成するための構文が用意されています。
自前でIEnumerable<T>IEnumerator<T>を作成する手間は基本的に不要と言えます。
(少しややこしいのですが、この構文は"Iterator block"と呼ばれます。)

// イテレータ構文と呼ばれているが、実際にはジェネレータ
static IEnumerable<int> Generate5Primes() {
    yield return 2;
    yield return 3;
    yield return 5;
    yield return 7;
    yield return 11;
}

// 無限ループも使い方次第
static IEnumerable<double> RandomDoubles() {
    var random = new Random();
    while (true) {
        yield return random.NextDouble();
    }
}

まとめ

"Gang of Four"によるデザインパターンの紹介が1994年のことになります。つまり、C#に限らずその後にリリースされた言語ではこれらのデザインパターンが言語に取り込まれていることも多いのです。
これからデザインパターンを勉強しようとしている人は、自分の得意とする言語(やフレームワーク)にどのようなデザインパターンが組み込まれているのかを調べてみると良いと思います。