インターレーターの仕組みが原因で、ラインyield break;
が実行されることはありません。
あなたがそうするとき、イテレータメソッドGetTimes(int count)
は実行されません
var times = GetTimes(2);
むしろ、そこから値を取得するたびに実行されます (たとえば、 を実行するときtimes.Take(1).Single().ToString()
)。
ここで、この一見奇妙な動作を生成するものは 2 つあります。
yield return
イテレータは、行にヒットするたびに停止します。イテレータの実行は、そこから別の要素を取得しようとすると、終了したポイントから再開されます。そうしないと、実行が再開されません。
実際にはイテレータを 2 回実行しています。通常、 「IEnumerable の複数の列挙」と呼ばれることを行います。
バックグラウンドで実際に何が起こっているかを説明するために、GetTimes
メソッドを少し変更してみましょう: 毎回同じ日付を返すのではなく、呼び出されるたびに前の日付 + 1 日を返します。Console.WriteLine
実行をトレースするためにいくつか追加してみましょう。したがって、新しいメソッド本体は次のようになります。
IEnumerable<DateTime> GetTimes(int count)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine("returning the value with index " + i);
yield return DateTime.Now.AddDays(i);
}
Console.WriteLine("About to hit the `yield break`! Awesome!");
yield break;
}
コードを実行すると、次の出力が生成されます。
インデックス 0 の値を返す
最初の要素:11/16/2012 11:34:46 PM
インデックス 0 の値を返す
インデックス 1 の値を返す
2 番目の要素:11/17/2012 11:34:46 PM
終了した...
これは、上記の 2 つの点を示しています。
GetTimes
値が返されるとすぐに実行が停止され、別の値が要求されるとすぐに同じ状態から再開されます。
Iterator は 2 回実行されます (2 回目に値を要求し、次の値を要求して使用times
します)。Skip
Take
わかりましたが、なぜyield break;
get が実行されないのですか? これは、イテレータが 2 つの値を生成でき、2 回しか呼び出されないため、2 回目のyield return
ヒット後にフリーズするためです。イテレータから 3 番目の要素を要求すると、break
行がヒットします。
foreach
ここで、最後の行にヒットするシナリオを説明するために、列挙子を通常の方法 (ループを使用) で使用してみましょう。Console.WriteLine
行を次のように置き換えます。
foreach (var dateTime in times)
Console.WriteLine(dateTime);
このコードは、次の出力を生成します。
インデックス 0 の値を返す
2012/11/17 12:05:20 午前
インデックス 1 の値を返す
2012/11/18 12:05:20 午前
「利回りブレーク」を迎えようとしています!素晴らしい!
ご覧のとおりforeach
、イテレータが最後まで消費され、yield break
行がヒットします。これは、ブレークポイントを設定して確認することもできます。