閉じる

C#のyield breakで列挙を終了する方法!returnとの違いも徹底解説

C#でデータの集まりを効率よく扱う際に欠かせないのがyieldキーワードです。

通常、要素を1つずつ返すにはyield returnを使用しますが、特定の条件で列挙を完全に終了させたい場合にはyield breakが重要な役割を果たします。

本記事では、初心者から中級者の方に向けて、yield breakの具体的な使い方や仕組み、そして通常のreturn文との決定的な違いについて、図解を交えながら詳しく解説します。

反復処理をよりスマートに制御するためのテクニックをマスターしましょう。

yield breakの役割と基本的な仕組み

C#におけるyield breakは、イテレータブロック(IEnumerableなどを返すメソッド)において、「これ以上返す要素がないこと」を呼び出し側に伝えるための命令です。

通常のメソッドであればreturnを使用して処理を終了させますが、yieldを使用しているメソッド内では、処理の終了方法が少し特殊になります。

まずはイメージ図で、列挙がどのように中断されるのかを確認してみましょう。

イテレータにおける終了の合図

yield returnを含むメソッドは、一度にすべての要素をメモリに展開するのではなく、必要になるたびに1つずつ要素を生成します。

これを遅延評価と呼びます。

この流れの中で、「もう次に渡せるデータはありません」と宣言するのがyield breakです。

これが実行されると、そのイテレータの実行状態は完了状態となり、呼び出し側のforeachループなどは即座に終了します。

基本的なコード例

実際にどのように記述するのか、簡単なプログラムを見てみましょう。

C#
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // GetNumbersメソッドから返される要素を列挙する
        foreach (int number in GetNumbers())
        {
            Console.WriteLine(number);
        }
    }

    // 数値を返すイテレータメソッド
    static IEnumerable<int> GetNumbers()
    {
        yield return 1;
        yield return 2;

        // ここで列挙を終了する
        yield break;

        // yield break以降のコードは実行されない(到達不能)
        yield return 3;
    }
}
実行結果
1
2

このコードでは、yield return 2;の後にyield break;が実行されています。

そのため、その後に記述されているyield return 3;は無視され、呼び出し側のforeachループは2を表示した時点で終了します。

yield breakとreturnの決定的な違い

C#を学び始めた方が最も混乱しやすいポイントは、「なぜreturnではなくyield breakを使うのか?」という点です。

実は、イテレータブロック内では通常のreturn文の使い方に制限があります。

1. 戻り値の有無

通常のメソッドで使われるreturnは、メソッドの戻り値の型(intやstringなど)に合わせた値を返します。

しかし、yield break値を返すことができません

イテレータメソッド(戻り値がIEnumerable<T>など)の中で、値を返そうとしてreturn 10;のように記述すると、コンパイルエラーが発生します。

イテレータで値を返すのはあくまでyield returnの役割であり、yield breakは「終了」という制御だけを担当します。

2. 処理の継続性

通常のメソッドはreturnが呼ばれた瞬間にスタックから消滅し、すべてのローカル変数が破棄されます。

一方で、イテレータはyield returnを呼び出した後も、「どこまで処理したか」という状態を保持し続けます。

yield breakはこの「保持されている状態」を破棄し、列挙を完全に終わらせる合図となります。

機能return文yield break文
使用場所通常のメソッドイテレータブロック内
値の返却可能(return 10; など)不可(終了の合図のみ)
メソッドの終了即座に終了する列挙(列挙子)を終了する
コンパイルエラーイテレータ内で値付きreturnは不可通常のメソッドでは使用不可

実践的な活用シーン:条件による列挙の終了

yield breakが真価を発揮するのは、ループ処理の中で特定の条件を満たしたときにデータの供給を止めたい場合です。

特定のキーワードで停止する例

例えば、リストの中から文字列を順番に返すが、特定の「終了ワード」が出現した瞬間にそれ以降の処理を一切行わないようにしたい場合に便利です。

C#
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var words = new List<string> { "Apple", "Banana", "STOP", "Cherry", "Date" };

        Console.WriteLine("読み込みを開始します...");
        foreach (var word in FilterWords(words))
        {
            Console.WriteLine($"取得: {word}");
        }
        Console.WriteLine("読み込みが終了しました。");
    }

    static IEnumerable<string> FilterWords(IEnumerable<string> source)
    {
        foreach (var item in source)
        {
            if (item == "STOP")
            {
                // "STOP"を見つけたら、以降の要素は見ずに列挙を終了
                yield break;
            }
            yield return item;
        }
    }
}
実行結果
読み込みを開始します...
取得: Apple
取得: Banana
読み込みが終了しました。

この例では、CherryDateといった要素が元のリストに存在していても、yield breakによって列挙そのものが打ち切られるため、それらが処理されることはありません。

無限ループの脱出

yield returnを使用して無限に続くシーケンス(例:乱数生成やカウントアップ)を作成する場合、外部からの条件や内部の制限値に達した際にyield breakで安全に終了させることができます。

yield breakとコンパイラの舞台裏(ステートマシン)

なぜyield breakが必要なのかを理解するために、少しだけ高度な話をします。

C#のコンパイラは、yieldを含むメソッドを見つけると、それを「ステートマシン(状態遷移機)」という複雑なクラスに自動的に書き換えます。

IEnumerator.MoveNext()との関係

foreachループが動作するとき、内部ではMoveNext()というメソッドが呼ばれています。

  • yield returnに到達した場合:MoveNext()trueを返します。
  • yield breakに到達した場合:MoveNext()falseを返します。

つまり、yield breakを呼び出すことは、呼び出し側のループに対して「これ以上MoveNext()を呼び出しても無駄ですよ(falseを返します)」と通知することと同義なのです。

この仕組みがあるおかげで、私たちは複雑な状態管理を自分で行うことなく、シンプルなコードで列挙の停止を制御できます。

yield breakを使用する際の注意点

非常に便利なyield breakですが、いくつか注意すべきルールがあります。

1. finallyブロックとの兼ね合い

yield breakが実行された場合でも、そのメソッド内にtry-finallyブロックがあれば、finally句の内容は実行されます

これはリソースの解放(ファイルのクローズなど)を確実に行うための重要な仕様です。

C#
static IEnumerable<int> GetNumbersWithCleanup()
{
    try
    {
        yield return 1;
        yield break; // ここで終了しても...
    }
    finally
    {
        // ...このブロックは必ず実行される
        Console.WriteLine("後処理を実行しました");
    }
}

2. ラムダ式内での使用

yield(return/break共に)は、匿名メソッドやラムダ式の中では使用できません

必ず名前付きのメソッド(またはローカル関数)として定義する必要があります。

LINQのWhereなどの引数に直接書くことはできないので注意しましょう。

応用例:早期リターンとしてのyield break

メソッドの冒頭で引数のチェックを行い、条件に合わない場合に「空のコレクション」を返したい場合にもyield breakは役立ちます。

C#
public IEnumerable<string> GetData(bool isValid)
{
    // 事前条件チェック
    if (!isValid)
    {
        // 何も返さずに終了(空のシーケンスになる)
        yield break;
    }

    yield return "Data1";
    yield return "Data2";
}

このように記述すると、isValidfalseのとき、呼び出し側は「要素が0個のリスト」を受け取ったのと同じ挙動になります。

まとめ

yield breakは、C#のイテレータにおいて列挙を安全かつ明示的に終了させるための必須キーワードです。

通常のreturnが「値を1つ返してメソッドを抜ける」のに対し、yield breakは「列挙の終了を通知してイテレータを完了させる」という異なる役割を持っています。

  • イテレータ内で処理を打ち切る場合はyield breakを使う
  • 値を返すのはyield returnの役割
  • yield breakが呼ばれてもfinallyブロックは実行される
  • 空のシーケンスを返したいときの早期リターンとしても有効

これらの特性を理解して使いこなすことで、メモリ効率が良く、かつ読みやすいデータ処理コードを記述できるようになります。

ぜひ日々のコーディングに活用してみてください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!