C#を使用した開発において、配列やリストなどのコレクションを順番に処理するforeach文は非常に頻繁に利用されます。

通常、foreach文はすべての要素を走査しますが、特定の条件を満たした時点で処理を切り上げたいケースも少なくありません。

このような場面で不可欠なのがbreak文によるループの中断です。

本記事では、C#のforeach文でbreakを使用する方法を、初心者にも分かりやすく解説します。

また、混同しやすいcontinue文との違いや、多重ループにおける注意点、さらには実戦で役立つ応用テクニックまで詳しく網羅しました。

効率的なコーディングスキルを身につけるための参考にしてください。

C#のforeach文におけるbreakの役割

C#のforeach文は、コレクション内の要素を一つずつ取り出して処理を行う反復ステートメントです。

通常、コレクションの末尾に到達するまでループは継続されますが、特定の条件に合致した瞬間にループ全体を終了させたい場合break文を使用します。

break文が実行されると、ループ内の残りの処理および、まだ取り出されていない要素の反復処理はすべてスキップされ、プログラムの制御はforeachブロックの直後の命令へと移ります。

これにより、不要な計算リソースの消費を抑え、プログラムの実行速度を向上させることが可能です。

基本的な構文と動作

foreach文の中でif文などの条件分岐と組み合わせてbreakを記述するのが一般的な形です。

C#
foreach (var item in collection)
{
    if (中断条件)
    {
        // ループを直ちに終了する
        break;
    }
    // 中断条件に合致しない場合の処理
}

具体的なサンプルコード

以下のプログラムは、整数のリストから特定の数値を探し、見つかった時点でループを抜ける例です。

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

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 10, 25, 40, 55, 70, 85, 100 };
        int target = 55;

        Console.WriteLine("探索を開始します。");

        foreach (int num in numbers)
        {
            Console.WriteLine($"現在の値: {num}");

            if (num == target)
            {
                // ターゲットが見つかったのでループを中断
                Console.WriteLine($"{target} が見つかりました。処理を中断します。");
                break;
            }
        }

        Console.WriteLine("foreach文の外に抜けました。");
    }
}
実行結果
探索を開始します。
現在の値: 10
現在の値: 25
現在の値: 40
現在の値: 55
55 が見つかりました。処理を中断します。
foreach文の外に抜けました。

この例では、リストの中に「100」まで要素がありますが、「55」に到達した時点でループが終了していることがわかります。

残りの「70, 85, 100」の処理は行われません。

breakとcontinueの決定的な違い

break文と並んでよく使われるのがcontinue文です。

これらはどちらもループの流れを制御するものですが、その効果は大きく異なります。

continue文の動作

continue文は、「現在の反復処理のみをスキップし、次の要素の処理へ進む」ために使用されます。

ループ自体を終了させるわけではなく、あくまで「今の回」を飛ばすだけです。

比較表による整理

両者の違いを理解するために、以下の表にまとめました。

項目break文continue文
主な目的ループ全体の強制終了現在の回の処理をスキップして次へ
実行後の動き閉じる中カッコ } の次へ移動ループの先頭(次の要素取得)へ移動
残りの要素処理されない処理される
主な用途検索完了時、異常検知時特定条件のデータを除外したい時

以下のコードで、挙動の違いを確認してみましょう。

C#
using System;

class Program
{
    static void Main()
    {
        int[] data = { 1, 2, 3, 4, 5 };

        Console.WriteLine("--- breakの動作 ---");
        foreach (int i in data)
        {
            if (i == 3) break;
            Console.WriteLine(i);
        }

        Console.WriteLine("--- continueの動作 ---");
        foreach (int i in data)
        {
            if (i == 3) continue;
            Console.WriteLine(i);
        }
    }
}
実行結果
--- breakの動作 ---
1
2
--- continueの動作 ---
1
2
4
5

breakは「3」が出た瞬間に終わりますが、continue「3」だけを飛ばして「4」と「5」の処理を継続しています。

ネスト(入れ子)構造におけるbreakの挙動

複雑なプログラムでは、foreach文の中にさらに別のforeach文やfor文が記述される「ネスト」の状態になることがあります。

ここで注意が必要なのは、break文は「現在実行されている最も内側のループ」しか脱出できないという点です。

外側のループも抜けたい場合

内側のループで条件を満たした際、外側のループまで一気に抜けたい場合には工夫が必要です。

主に以下の3つの方法が検討されます。

  1. フラグ変数を使用する: 内側でフラグを立て、外側でもそのフラグをチェックしてbreakする。
  2. return文を使用する: 処理自体がメソッドとして独立しているなら、returnでメソッドごと終了する。
  3. LINQを使用する: データを平坦化してから処理する。

以下はフラグ変数を用いた例です。

C#
using System;

class Program
{
    static void Main()
    {
        string[][] matrix = {
            new string[] { "A1", "A2", "A3" },
            new string[] { "B1", "TARGET", "B3" },
            new string[] { "C1", "C2", "C3" }
        };

        bool found = false;

        foreach (var row in matrix)
        {
            foreach (var cell in row)
            {
                if (cell == "TARGET")
                {
                    Console.WriteLine("ターゲットを発見しました。");
                    found = true;
                    break; // 内側のループを抜ける
                }
                Console.WriteLine($"探索中: {cell}");
            }

            if (found)
            {
                // フラグが立っていれば外側のループも抜ける
                break;
            }
        }
        Console.WriteLine("全探索を終了しました。");
    }
}
実行結果
探索中: A1
探索中: A2
探索中: A3
探索中: B1
ターゲットを発見しました。
全探索を終了しました。

foreachとbreakの活用シーン

実務においてforeachbreakを組み合わせるパターンは定型化されています。

代表的なケースを見ていきましょう。

検索処理の最適化

最も一般的な用途は、大量のデータから特定の条件に合うものを1つだけ探すケースです。

例えば、ユーザーリストからIDが一致するユーザーを検索する場合、見つかった後に残りの数万件をループさせるのは非常に非効率です。

見つかった瞬間にbreakさせることで、計算リソースを劇的に節約できます。

エラー検知時の即時中断

複数のファイルを順番にアップロードするようなバッチ処理において、1つでも致命的なエラーが発生したら後続の処理を中止したい場合があります。

このような際、try-catchbreakを組み合わせて使用します。

C#
foreach (var file in files)
{
    bool success = UploadFile(file);
    if (!success)
    {
        Console.WriteLine("アップロードに失敗したため、以降の処理を中止します。");
        break;
    }
}

モダンなC#におけるLINQとの使い分け

近年のC#開発では、foreachbreakを使った命令的な記述の代わりに、LINQ (Language Integrated Query)を用いた宣言的な記述が好まれる傾向にあります。

例えば、「リストから最初の偶数を見つける」という処理は、以下のように書き換えられます。

従来の方式:

C#
int firstEven = -1;
foreach (int n in numbers)
{
    if (n % 2 == 0)
    {
        firstEven = n;
        break;
    }
}

LINQを使用した方式:

C#
using System.Linq;
// ...
int firstEven = numbers.FirstOrDefault(n => n % 2 == 0);

LINQのFirstOrDefaultAnyTakeといったメソッドは、内部的に効率的なループ処理を行っており、条件を満たした時点で列挙を停止する(遅延評価)性質を持っています。

コードの可読性が高まるため、単純な検索やフィルタリングであればLINQの利用を検討すべきです。

ただし、ループ内で行う処理が複雑であったり、複数の副作用(ログ出力や状態更新など)を伴う場合は、依然としてforeach文とbreakを組み合わせる方が、デバッグしやすく分かりやすいコードになることもあります。

状況に応じて最適な手法を選択することが重要です。

まとめ

C#のforeach文におけるbreakは、ループ処理の制御を柔軟に行うための基本かつ強力なツールです。

  • break文は、ループ全体をその場で即座に終了させる。
  • continue文は、現在の反復だけをスキップして次の要素へ進む。
  • ネスト構造では、最も内側のループのみが対象となるため、多重ループ脱出にはフラグ管理などが必要。
  • シンプルな検索処理には、LINQによる代替も検討する。

これらの特性を正しく理解し、適切に使い分けることで、バグの少ない、そしてパフォーマンスに優れたC#プログラムを記述できるようになります。

ループ処理はプログラミングの根幹を成す要素ですので、ぜひこの機会にマスターしてください。