「yield return ってコード短くなるよね」 くらいの認識しか持ってなかったんですが、その程度の話じゃなかったんですね。
放課後のVB中学校のこちらのエントリを読んで、いたく感動しました。
全然ダメだな>自分 (・・・自虐ネタはこのくらいにして)
yield return はコレクションに対する MoveNext のタイミングで呼び出されます。"遅延実行" です。
コードで言うと、こういうこと。
static void Main(string[] args)
{
IEnumerable list1 = GetNumbers1();
IEnumerable list2 = GetNumbers2();
Console.WriteLine("*** 結果出力前 ***");
foreach (int i in list1)
Console.WriteLine(i);
foreach (int i in list2)
Console.WriteLine(i);
Console.ReadLine();
}
public static IEnumerable GetNumbers1()
{
Console.WriteLine("GetNumbers1 呼び出し");
return new List() { 1, 2, 3 };
}
public static IEnumerable GetNumbers2()
{
Console.WriteLine("GetNumbers2 呼び出し");
yield return 1;
yield return 2;
yield return 3;
}
GetNumber2 は list2 に代入するタイミングでは実行されません。実行されるのは foreach のところ。
上のコードを実行してみると確かにこんな結果になります。
GetNumber2 が foreach で呼び出されるに対して GetNumbers1 は list1 に代入しているタイミングで呼び出されているのもわかります。
これらの違いはそれぞれの foreach をコメントアウトするともっとわかりやすくなります(意味のないコードのなるけど)。
GetNumber1 は呼び出されるが、GetNumber2 は foreach のところをコメントアウトすると結局呼び出されることはありません。
遅延実行されるということは、コレクションの中身はあらかじめ決まっている必要はなくて、残りの要素(まだ読み出していない要素)を動的に変更することも可能だということ。
これって使い道ありますね、きっと。
面白い~。
ついでに言うと LINQ でも同じことが起こります。LINQ は yield return しているということです。
var q = from ~
なんてコードを書いた時にその場で実行されそうな気がしますが、そうではなくて LINQ の式が実行されるのは q を利用しようとした時です。
foreach するなりコントロールをバインディングするなりしたタイミングで初めて実行されるわけです。
そういえばセミナーとかで LINQ の話を聞いた時、確かに実行時のタイミングについて聞いた覚えがあります。その時は「へぇ~」で終わってしまって、その後すっかり頭の中から消し去ってました。
ちなみに yield は MoveNext ごとに呼び出されるという話は「プログラミングVisual C# 2005」(日経BPソフトプレス刊)にちゃんと載ってました。持ってるだけでちゃんと読んでないと、こういうことを今更知ることになるわけです(最後も自虐ネタで)。