本文へスキップ
BecomeCoder

C#文法コース · 第3章 コレクション · レッスン27

yield と IEnumerable(イテレータ)

ブラウザで完結

導入

foreachで回せるものは、すべてIEnumerable<T>という「列挙できる」共通の形をしています。じつは自分でも、yield returnを使えば1件ずつ値を生成する列を簡単に作れます。これをイテレータと呼び、LINQの遅延実行(必要になるまで計算しない)の正体でもあります。

図解

flowchart LR
    F["foreach が1件求める"] --> Y["yield return で1件返す"]
    Y --> P["続きの状態を覚えたまま一時停止"]
    P -->|次を求められたら| Y
    Y -.->|必要な分だけ生成| L["遅延評価(無限列もOK)"]

サンプル

// yield return で「1件ずつ生成する」独自の列を作れる
IEnumerable<int> Squares(int n)
{
    for (int i = 1; i <= n; i++)
    {
        yield return i * i;   // ここで1件返し、次に呼ばれたら続きから再開する
    }
}

foreach (int sq in Squares(4))
    Console.Write($"{sq} ");   // 1 4 9 16
Console.WriteLine();

// 遅延評価: 無限に続く列でも、必要な分だけ取り出せる
IEnumerable<int> Naturals()
{
    int n = 1;
    while (true) yield return n++;   // 無限ループだが…
}

foreach (int x in Naturals().Take(3))   // Take(3) で先頭3件だけ取り出す
    Console.Write($"{x} ");   // 1 2 3
  • IEnumerable<T>は「foreachで1件ずつ取り出せる」共通の型
  • yield returnで値を返すと、メソッドは状態を覚えたまま一時停止し、次に呼ばれたら続きから再開する
  • 必要な分だけ生成する遅延評価なので、無限列でもTakeなどと組み合わせて安全に扱える

演習

// TODO: yield return を使って 3, 2, 1 を順に返す CountDown() を定義し、
//       foreach で空白区切り出力してください("3 2 1 ")
IEnumerable<int> CountDown()
{
    ___
}

foreach (int n in CountDown())
    Console.Write($"{n} ");
  • 期待される出力: 3 2 1
ヒント1を見る

for (int i = 3; i >= 1; i--) yield return i;

ヒント2を見る

CountDown の中身を for (int i = 3; i >= 1; i--) { yield return i; } にします

まとめ

  • foreachで回せるものの正体はIEnumerable<T>
  • yield returnで「1件ずつ生成する列(イテレータ)」を自作できる
  • 必要な分だけ計算する遅延評価は、LINQの遅延実行と同じ仕組み

次回: ここまでの入れ物を「どう選ぶか」を整理します。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。