C#のforeach文の展開について
C#のforeach文は配列をはじめ、列挙子やリストなどのさまざまなシーケンシャルな反復処理を簡潔に書くことができます。
foreach (ElementType element in collection) { statement; }
このforeach文はコンパイル時に以下のようなコードに展開されます。
E enumerator = (collection).GetEnumerator(); try { while (enumerator.MoveNext()) { ElementType element = (ElementType)enumerator.Current; statement; } } finally { IDisposable disposable = enumerator as System.IDisposable; if (disposable != null) disposable.Dispose(); }
展開されたコードを見ると、collectionはGetEnumerator()メソッドを実装している必要がありそうです。
また、GetEnumerator()メソッドの戻り値型であるEはMoveNext()メソッドとenumerator.Currentプロパティを実装している必要がありそうです。
実際にこのような仕様になっています。
たとえば、フィボナッチ数列を表すクラスは以下のように実装すればforeach文で扱うことが出来ます。
■ソースコード
class FibonacciNums { public class ElementEnum { private int _num; private int _index; private int[] _value; // コンストラクタ public ElementEnum(int num, int initValue0, int initValue1) { // 配列初期化 _num = num; _index = 0; _value = new int[2] { initValue0, initValue1 }; } // 次に移動する public bool MoveNext() { // 最後まで移動していたら秋涼 if (_index >= _num) { return false; } // インデックス更新 _index++; return true; } // 現在の要素の値を返す public int Current { get { if (_index <= 2) { // 最初の2つはそのまま返す return _value[_index - 1]; } // 前の2つの数の和から次の数を求める int newValue = _value[0] + _value[1]; _value[0] = _value[1]; _value[1] = newValue; return newValue; } } } private int _num; private int _initValue0; private int _initValue1; // コンストラクタ public FibonacciNums(int num, int initValue0, int initValue1) { _num = num; _initValue0 = initValue0; _initValue1 = initValue1; } // Enumeratorを取得する public ElementEnum GetEnumerator() { return new ElementEnum(_num, _initValue0, _initValue1); } }
class Program { static void Main(string[] args) { // 10個のフィボナッチ数を出力する FibonacciNums fibonacciNums = new FibonacciNums(10, 1, 1); foreach (var value in fibonacciNums) { System.Console.WriteLine(value); } } }
■実行結果
1 1 2 3 5 8 13 21 34 55
クラスの実装はやや手間がかかるかと思いますが、一度実装してしまえばこちらのものです。
ライブラリでもforeach文での使用が可能なクラスはたくさん有るので、どのような実装になっているか調べてみるのも面白いかもしれません。
■参考サイト
8.8.4 foreach ステートメント