【C#】for・foreach・while完全ガイド|ループ処理の使い分け・break/continue・ネストループ・do-whileまで

【C#】for・foreach・whileのループ処理の使い方 C#

プログラムの中で「同じ処理を繰り返す」ことは日常的な作業です。C# にはその目的に応じた複数のループ構文が用意されており、状況に合ったものを選ぶことでコードの意図が明確になります。

本記事では forforeachwhiledo-while の4種類を網羅し、break/continue の使い方、多重ループのパターン、そして「どのループをいつ使うか」の判断基準まで解説します。

スポンサーリンク

for文:回数が決まっているループ

for 文は「何回繰り返すか」があらかじめわかっている場合に最も適したループです。初期化・条件・更新の3つを1行にまとめて書けます。

for文の基本構文
// for (初期化; 継続条件; 更新)
for (int i = 0; i < 5; i++)
{
    Console.WriteLine($"i = {i}");
}
// 出力: i = 0, i = 1, i = 2, i = 3, i = 4

配列・リストのインデックスアクセス

配列の添字を直接操作したいとき、for文はforeachより適しています。

インデックスを使ったアクセス
string[] fruits = { "Apple", "Banana", "Orange", "Grape" };

for (int i = 0; i < fruits.Length; i++)
{
    Console.WriteLine($"[{i}] {fruits[i]}");
}
// [0] Apple  [1] Banana  [2] Orange  [3] Grape

逆順ループ・ステップ変更

逆順・ステップ変更
// 逆順(末尾から先頭へ)
for (int i = 4; i >= 0; i--)
{
    Console.Write($"{i} "); // 4 3 2 1 0
}

// 2刻みで進む
for (int i = 0; i <= 10; i += 2)
{
    Console.Write($"{i} "); // 0 2 4 6 8 10
}

// 複数変数を同時に動かす
for (int i = 0, j = 10; i < j; i++, j--)
{
    Console.WriteLine($"i={i}, j={j}");
}
配列末尾から削除・置換するアルゴリズムでは逆順ループが必須です。順方向に削除するとインデックスがずれてバグになります。

foreach文:コレクションを順番に処理する

foreach 文はコレクション(配列・List・Dictionary など)の全要素を順番に処理するときに使います。インデックス管理が不要なぶんコードがシンプルになります。

foreach文の基本
List<string> names = new List<string> { "Taro", "Hanako", "Jiro" };

foreach (string name in names)
{
    Console.WriteLine($"こんにちは、{name}さん");
}

Dictionaryのforeachはキーと値を同時に取り出せる

Dictionaryのforeach
var scores = new Dictionary<string, int>
{
    { "国語", 85 },
    { "数学", 92 },
    { "英語", 78 }
};

foreach (var pair in scores)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}点");
}

// C# 7以降: 分解構文で書くとより読みやすい
foreach (var (subject, score) in scores)
{
    Console.WriteLine($"{subject}: {score}点");
}

インデックスが必要なforeach(C# 7以降)

インデックスも一緒に取得したい場合は Select を使うか、for 文に切り替えます。

インデックス付きforeach
string[] colors = { "Red", "Green", "Blue" };

// LINQ の Select を使う方法
foreach (var (color, index) in colors.Select((c, i) => (c, i)))
{
    Console.WriteLine($"[{index}] {color}");
}

// シンプルに for 文を使う(インデックスが必要なら for の方が自然)
for (int i = 0; i < colors.Length; i++)
{
    Console.WriteLine($"[{i}] {colors[i]}");
}
foreach中にコレクションを変更することはできません
foreach でループ中に要素を追加・削除すると InvalidOperationException が発生します。変更が必要な場合は for 文で逆順にループするか、削除対象をリストに収集してループ後に処理します。

while文:条件が満たされる間繰り返す

while 文は繰り返し回数が事前にわからず、「ある条件が true の間だけ繰り返す」ときに使います。

while文の基本
int count = 1;

while (count <= 5)
{
    Console.WriteLine($"カウント: {count}");
    count++;
}

ファイル読み込みパターン

行末まで読み続けるファイル処理は while の典型的な使い方です。

ファイルの全行読み込み
using var reader = new System.IO.StreamReader("data.txt");
string? line;

while ((line = reader.ReadLine()) != null)
{
    Console.WriteLine(line);
}

無限ループと break による脱出

意図的に無限ループを作り、内部で break して脱出するパターンも実務でよく使われます。

無限ループ + break
while (true)
{
    Console.Write("コマンドを入力してください (exit で終了): ");
    string? input = Console.ReadLine();

    if (input == "exit")
        break;

    Console.WriteLine($"入力: {input}");
}
Console.WriteLine("終了しました");

do-while文:最低1回は必ず実行する

do-while 文は条件の判定がループ末尾にある後判定ループです。条件にかかわらず最低1回は必ずブロックが実行されます。

do-while の基本構文
int number = 0;

do
{
    Console.Write("1〜10の数値を入力してください: ");
    string? input = Console.ReadLine();
    int.TryParse(input, out number);
}
while (number < 1 || number > 10); // 入力が範囲外なら繰り返す

Console.WriteLine($"入力された値: {number}");
do-while の主な使いどころ
・ユーザー入力の再試行(「正しい値を入力するまで繰り返す」)
・メニュー画面の表示(必ず1回は選択肢を表示してから判定する)
処理を必ず1回実行してから条件を確認する構造に自然にはまります。

break と continue でループを制御する

break:ループを即座に終了する

breakの使用例
int[] numbers = { 3, 7, 2, 9, 4, 1, 8 };

// 目的の値が見つかったらそれ以降の検索をやめる
int target = 9;
int foundAt = -1;

for (int i = 0; i < numbers.Length; i++)
{
    if (numbers[i] == target)
    {
        foundAt = i;
        break; // 見つかった時点でループを抜ける
    }
}

Console.WriteLine(foundAt >= 0 ? $"インデックス {foundAt} に見つかりました" : "見つかりませんでした");

continue:現在のイテレーションをスキップする

continueの使用例
// 偶数だけを処理し、奇数はスキップ
for (int i = 0; i < 10; i++)
{
    if (i % 2 != 0)
        continue; // 奇数は残りの処理をスキップして次のイテレーションへ

    Console.Write($"{i} "); // 0 2 4 6 8
}
continue のメリット:ガード節としての使い方
continue を使うと「処理したくない条件を先頭で弾く」ことができ、ネストを減らして読みやすくなります。条件に合致するものだけを深いブロックで処理するより、合致しないものを continue で早期スキップする書き方が推奨されます。

多重ループ(ネストループ)

2次元配列の処理

2次元配列の走査
int[,] matrix = {
    { 1, 2, 3 },
    { 4, 5, 6 },
    { 7, 8, 9 }
};

for (int row = 0; row < 3; row++)
{
    for (int col = 0; col < 3; col++)
    {
        Console.Write($"{matrix[row, col],3}"); // 幅3で右揃え
    }
    Console.WriteLine();
}
//  1  2  3
//  4  5  6
//  7  8  9

多重ループから一気に抜ける

C# にはラベル付き break がないため、多重ループを一気に抜ける場合は フラグ変数 または メソッドへの切り出し が定石です。

多重ループから一気に抜ける方法
// 方法①: フラグ変数を使う
bool found = false;
for (int i = 0; i < 5 && !found; i++)
{
    for (int j = 0; j < 5; j++)
    {
        if (i * j == 6)
        {
            Console.WriteLine($"見つかった: i={i}, j={j}");
            found = true;
            break;
        }
    }
}

// 方法②: メソッドに切り出して return(推奨:コードが一番クリーン)
static (int, int) FindProduct(int target)
{
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 5; j++)
            if (i * j == target)
                return (i, j); // return で多重ループを一度に抜けられる
    return (-1, -1);
}
goto 文は使わないこと
C# には goto 文があり多重ループからの脱出に使えますが、コードの流れが追いにくくなるため実務では禁忌とされます。メソッド分割やフラグ変数で解決してください。

for・foreach・while・do-while の使い分け

構文 よく合うケース 具体例
for 繰り返し回数が明確・インデックスが必要・逆順や2刻みで進む 配列の要素を末尾から削除・九九の表を出力
foreach コレクション全要素を順に処理・インデックス不要 List の全要素を表示・Dictionary を巡回
while 繰り返し回数が不明・条件が複雑・ファイルや入力ストリームの読み込み ファイルの全行読み込み・外部APIのポーリング
do-while 最低1回は必ず実行・再入力・メニュー画面 「正しい値が入力されるまで繰り返す」バリデーション
迷ったときの選び方
1. コレクション全体を処理するなら → foreach(最もシンプル)
2. 回数が決まっているなら → for(インデックスも使えて汎用的)
3. 回数が決まっていないなら → while
4. 最低1回は実行が必要なら → do-while

LINQ との使い分け

コレクションのフィルタリング・変換・集計には foreach ループより LINQ が適している場面があります。ループを書く前に「これは LINQ で書けないか?」を意識すると、より宣言的で読みやすいコードになります。

foreach vs LINQ
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// foreach で偶数の合計を求める(命令的スタイル)
int sumLoop = 0;
foreach (var n in numbers)
{
    if (n % 2 == 0)
        sumLoop += n;
}

// LINQ で同じことを書く(宣言的スタイル)
int sumLinq = numbers.Where(n => n % 2 == 0).Sum();

Console.WriteLine(sumLoop); // 30
Console.WriteLine(sumLinq); // 30
LINQ の詳細な使い方はLINQの基本(Where・Select)LINQの応用(GroupBy・OrderBy・Join)を参照してください。

よくある落とし穴と注意点

オフバイワンエラー(Off-by-one)

配列のインデックスは 0 始まりなので、i <= arr.Length と書くと最終要素の次(存在しない要素)にアクセスして IndexOutOfRangeException が発生します。条件は必ず i < arr.Length と書き、<= を使う場合は i <= arr.Length - 1 になります。

foreach中のコレクション変更

foreach でループ中に要素を追加・削除すると InvalidOperationException: コレクションが変更されました という例外が発生します。削除対象を別リストに収集してからループ外で削除するか、for 文で逆順にループする方法を使います。

無限ループ(ループ変数を更新し忘れる)

while 文でカウンタ変数の更新を忘れると無限ループになります。for 文は更新式をヘッダーに書くので忘れにくいですが、while 文ではループボディ内の更新忘れに注意してください。また、while (true) の無限ループでは必ず break または return の脱出条件を設けましょう。

浮動小数点のカウンタ変数

for 文のカウンタ変数に doublefloat を使うと、浮動小数点誤差で想定と異なる回数ループしたり、終了条件に到達しない場合があります。カウンタには整数型(int/long)を使い、小数のステップが必要なら整数インデックスから計算します。

LINQ クエリの遅延評価に注意

foreach でLINQクエリを反復すると、毎回クエリが再評価されます。同じクエリ結果を複数回使う場合は .ToList().ToArray() で具体化(マテリアライズ)してからループすることで、不要な再評価を防げます。

よくある質問

Qfor文とforeach文はどちらが速いですか?
A配列に対しては for 文がわずかに高速なケースがあります。List<T> に対してはほぼ同等です。ただし実務レベルでの差は無視できる程度で、可読性を優先して選ぶのが基本です。パフォーマンスが問題になった場合はプロファイリングで確認してから最適化します。
Qforeachでインデックスを取得するにはどうしますか?
A方法は2つあります。①for 文に切り替える(インデックスが必要なら for が自然)、② LINQ の Select((item, index) => (item, index)) で分解構文を使う。どちらが読みやすいかはケースバイケースですが、単純な添字アクセスなら for 文の方がシンプルです。
Qwhileとdo-whileはどう使い分けますか?
A「条件を確認してから実行するか(while)」「とにかく1回実行してから条件を確認するか(do-while)」の違いです。ユーザー入力の再試行やメニュー表示のように「まず実行、正しくなければやり直す」という構造にはdo-whileが自然に合います。それ以外の大半のケースはwhile文を使います。
QC#でラベル付きbreakは使えますか?
AC#には Java のようなラベル付き break はありません。多重ループを一気に抜ける方法は①メソッドに切り出して return(推奨)、②フラグ変数で外側のループも制御する、③例外を使う(非推奨)の3つです。goto 文は技術的には使えますが実務では禁忌とされます。
Qforループのカウンタ変数にvarは使えますか?
A使えます。for (var i = 0; i < 10; i++) と書くと iint と推論されます。ただし var を使うメリットが少なく、チームによっては int i と明示する規約の場合もあります。読みやすさを優先して判断してください。

まとめ

構文 特徴・使いどころ 注意点
for 回数固定・インデックスが必要・逆順/ステップ変更 境界値は < を使う(≦ はオフバイワンの温床)
foreach コレクション全要素を順に処理・シンプル ループ中にコレクションを変更できない
while 回数不定・条件による繰り返し・ストリーム読み込み ループ変数の更新忘れで無限ループ
do-while 最低1回実行・入力バリデーション・メニュー 末尾セミコロン(while(...) ;)を忘れない
break ループを即座に終了・検索処理に必須 多重ループはメソッド分割で対処
continue 現在のイテレーションをスキップ・ガード節として使う 多用するとループの流れが追いにくくなる

コレクションのフィルタリング・変換・集計には foreach の代わりに LINQ を検討しましょう。LINQの基本(Where・Select)で宣言的なコレクション処理を習得すると、ループを書く場面がさらに減ります。