『究極のC#プログラミング』 Chapter3 yield returnとForEach その2
1つのクラスに複数の列挙機能をつける
int[]な配列を表すクラスを列挙するには、
public IEnumerator<int> GetEnumerator
というメソッドを定義すると良い。GetEnumeratorという名前のメソッドは、
foreach(int i in new Class1())
のようにした時に暗黙的に呼び出される。
そうでなく、
public IEnumerable<int> Method1 // IEnumera"tor"ではない
のようにメソッドを定義すると、これはenumeratorではなく、enumerableなオブジェクトを作成するので、
foreach(int i in new Class1().Method1())
という形で列挙することになる
列挙子ファクトリ
IEnumerable<T>
を列挙可能インターフェースと言う。これは列挙子ファクトリという機能を持つ。
これは列挙を行うオブジェクト、つまり列挙子を作るファクトリとして機能するということ。
だから、列挙子ファクトリは多重利用できる。例えば、foreachを入れ子にして
var range = Class1(0, 10); foreach(int j in range) { foreach(int i in range) { // code } }
としても良い。
yield returnはcatchできない
yield breakはcatchできるが、yield returnはcatchできない。
yield returnをtry catchの中に書くとコンパイルエラーになる。
catchの外に書くために、面倒だがtry finally構文を次のように併用する必要がある。
private static IEnumerable<string> readText(string filename) { var reader = File.OpenText(filename); try { for(;;) { string s; try { s = reader.ReadLine(); } catch(IOException e) { Console.WriteLine(e.ToString()); yield break; // yield breakはtry catch構文の中に書ける } if(s == null) break; yield return s; } } finally { read.Close(); } }
ForEachメソッド
コレクションの全ての要素に対して、指定したラムダ式(またはデリゲート)を実行する。
Array.ForEachが代表的。
int[] array = { 1, 2, 3 }; int sum = 0; Array.ForEach<int>(array, (i) => // Array.ForEach<> { sum += i; // ラムダ式は上のブロックのスコープに属するので、このように書ける });
他に、ジェネリックコレクションのクラスの持つForEachメソッドもあるし、
ForEachを自作することもできる。
列挙子とForEachの比較
ForEachのほうが数倍速い。
しかし、LINQにおいては「列挙できるものはクエリできる」ので、列挙子を作ることには重要な意味がある。