break はループを早めに終わらせます。continue は現在の反復の残りを飛ばして次の反復へ進みます。どちらも最も内側のループに作用し、for、foreach、while、do while で同じように動きます。
出力:
process order of 45
skip invalid total 0
process order of 120
skip invalid total -5
process order of 80
test order found, stopping
after the loop
continue は 0 と -5 のときに処理の行を飛ばしました。break は 999 でループを止めたので、60 は訪れられず、実行はループの後の最初の文から再開しました。
break:答えがわかったら止める
break の典型的な使い方は探索です。探していたものが見つかったら、残りを見る理由はありません。
出力:
first invalid email at index 1
break の後に反復子(i++)は実行されないので、i をループの外で宣言していれば、一致したインデックスがそのまま残ります。単純な探索なら、ライブラリの呼び出しでも同じことを表せます。Array.FindIndex(emails, e => !e.Contains("@")) はインデックス(または-1)を返し、LINQの emails.Any(e => !e.Contains("@")) は不正なものが存在するかどうかを答えます。
continue:次の反復へ進む
continue はループのためのガード節です。本来の処理を if で囲む代わりに、不要なケースを先頭で弾きます。
出力:
pen costs 1.20
mug costs 8.00
continue の次にどこへ進むかはループによって異なります。for ループでは反復子を実行してから条件を判定します。while ループでは条件へ直接ジャンプするので、典型的なバグにつながります。
int i = 0;
while (i < 10)
{
if (i % 2 == 0)
continue; // i++ below is skipped: i stays 0 forever
Console.WriteLine(i);
i++;
}
continue の前にカウンターを更新するか、更新がヘッダーにあって必ず実行される for ループを使います。
ループの中のswitchの中のbreak
switch の中では、break はcaseのセクションを終わらせます。その switch がループの中にあるとき、break はループではなくswitchに属します。
出力:
quit received
count = 2
"quit" のcaseがただの break だったら、switchが終わるだけでループは続き、count は3になっていたはずです。goto はループの後のラベルへジャンプします。ループをメソッドに移してそのcaseで return を書く方法でも動き、ラベルは不要です。
多重ループから抜ける
C#には、Javaの break outer; のようなラベル付きの break はありません。2重のループで探索するとき、内側のループでの break は内側のループを終わらせるだけで、外側のループは次の行へ進みます。標準的な直し方は3つあります。
1. フラグを外側のループで判定する:
出力:
first free seat: row 1, seat 2
2. goto で両方のループの後のラベルへ飛ぶ。「両方のループから抜ける」をそのまま表現するので、多くのC#のスタイルガイドが goto を認める唯一の場面です。
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if (seats[row, col] == 0)
{
Console.WriteLine($"first free seat: row {row}, seat {col}");
goto done;
}
}
}
done:
Console.WriteLine("search finished");
3. returnを使うメソッド。探索に名前が付き、結果を返せるので、たいてい最もすっきりします。
出力:
first free seat: row 1, seat 2
C# 7.0ではローカル関数も導入され、このヘルパーメソッドを呼び出し元のメソッドの中に置けるようになりました。ここで使ったoutパラメーターの形はrefとoutのページで説明しています。
gotoとラベル
ラベルは識別子の後にコロンを付けたもので、goto label; でそこへジャンプします。ルールは厳しく、最悪の誤用を防げるようになっています。
- ラベルは同じメソッド内で、
gotoと同じブロックかそれを囲むブロックになければなりません。ブロックの外へはジャンプできますが、中へは決してジャンプできません。外側からループの本体の中のラベルへジャンプすると、エラーCS0159(gotoステートメントのスコープ内にそのようなラベルがない)になります。 goto、break、continueでfinallyブロックから抜けることはできません。switchの中では、goto case 3;とgoto default;で別のセクションへ制御を移します。次のcaseへ暗黙に流れ込むとコンパイルエラーになるので、C#のswitchはこの方法でフォールスルーを表現します。
goto で(ラベルへ後ろ向きにジャンプして)ループを作ることもできますが、while ループなら同じことを誰でも見慣れた形で表せます。goto は多重ループから抜ける場合とswitchのセクションに限って使います。
return、throwとループ
return はメソッド全体から抜けるので、その中のすべてのループからも抜けます。throw も例外を発生させることで同じことをします。finally 付きの try ブロックの中では、break、continue、goto、return のどれでも、制御が移る前に finally ブロックが実行されます。だから、using ブロックでファイルを開いたままのループからでも安全に抜けられます。
| 文 | 抜けるもの | 続きの場所 |
|---|---|---|
continue | 現在の反復 | 次の反復(反復子、次に条件) |
break | 最も内側のループまたはswitch | その後の文 |
goto label | それを囲む任意のブロック | ラベル |
return | メソッド | 呼び出し元 |
throw | 一致する catch までのすべて | catch ブロック |
よくある質問
C#のbreakとcontinueの違いは何ですか?
break は最も内側のループ(または switch)をすぐに終わらせ、実行はその後から続きます。continue は現在の反復だけを終わらせて次の反復へ進みます。for ループでは次に反復子が実行され、while と do while では次に条件が判定され、foreach では次の要素が取得されます。
C#で多重ループから抜けるには?
C#にはラベル付きの break はありません。選択肢は3つです。bool のフラグを立てて外側のループで判定する、goto で両方のループの後のラベルへジャンプする、ループをメソッドに移してそこから return する。メソッドにする方法がたいてい最もすっきりしています。探索に名前と戻り値も与えられるからです。
C#でswitchの中のbreakはループから抜けますか?
抜けません。ループの中にある switch では、break は switch のセクションを終わらせるだけで、ループは続きます。caseの中からループを抜けるには、return、switchの後で判定するフラグ、またはループの後のラベルへの goto を使います。caseの中の continue は、ループの次の反復へ進みます。
C#でgotoは使えますか?
使えます。goto label; は同じメソッド内の label: へジャンプしますが、ブロックの中へジャンプすることや、finally から抜け出すことはできません。switch の中では、goto case value; と goto default; でセクション間をジャンプします。多重ループから抜ける場合とswitchの明示的なフォールスルー以外では、gotoが最も明確な選択になることはまれです。