『究極の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においては「列挙できるものはクエリできる」ので、列挙子を作ることには重要な意味がある。