while ループは、条件が真である限り本体を繰り返します。残高が目標に届くまで、キューが空になるまで、入力がなくなるまでというように、必要な反復回数が事前にわからないときに使います。
出力:
Year 1: 1070.00
Year 2: 1144.90
Year 3: 1225.04
Year 4: 1310.80
Year 5: 1402.55
Year 6: 1500.73
Goal reached after 6 years
条件は最初を含めて各反復の前に確認されます。savings がすでに目標に達していれば、本体は一度も実行されず、プログラムは「after 0 years」と表示します。ループは、条件が読む何かを本体が変えることに依存しています。ここでは savings が毎年増え、やがて savings < goal がfalseになります。
do while:少なくとも1回実行する
do while ループは判定を末尾に移すので、本体は常に少なくとも1回実行されます。
出力:
Attempt 1: got 7
Attempt 2: got 4
Attempt 3: got 1
Done after 3 attempts
code の最初の値は本体が1回実行された後にしか存在しません。まさに do while が合う場面です。構文上の注意点が2つあります。
while (code != 1)の後のセミコロンは必須です。省くとコンパイルエラーになります。- 条件で使う変数は
doの前に宣言しなければなりません。波かっこの中で宣言した変数は条件が実行される時点でスコープの外にあるので、do { int code = ...; } while (code != 1);はコンパイルできません。
同じループを while で書くと、ループの前で最初の呼び出しを重複させるか、最初の実行を強制するためのダミーの初期値が必要になります。do while は「試してから、もう一度試すか決める」をそのまま表現します。
番兵まで入力を読み込む
停止を表す単語まで、または入力が終わるまで行を読み込むのは、教科書どおりの while ループです。Console.ReadLine() は入力がなくなると null を返すので、両方を判定します。
出力:
skipping "abc": not a number
3 amounts, total 49.50
(line = Console.ReadLine()) != null は1つの式の中で次の行を代入して判定します。!= は = より優先順位が高いので、代入を囲むかっこは必須です。ループは done で止まるので、その後の 99 は読まれません。decimal.TryParse は例外を投げずに abc を拒否し、continue が次の行に進みます。null のチェックがないと、ReadLine は null を返し続けるので、入力の終わりに達した後は TryParse に null を永遠に渡し続けます。
do while は、入力を促して検証する形に向いています。この形では促すメッセージが少なくとも1回は表示されなければなりません。
出力:
Enter your age:
"-4" is not a valid age
Enter your age:
"fifteen" is not a valid age
Enter your age:
Age accepted: 15
促すメッセージはループの中にあるので、拒否された回答のたびに繰り返され、age は do の前に宣言されているのでループの後のコードで使えます。キーボードの前にユーザーがいる場合は、各回答がメッセージの後にコンソールに表示されます。ここのように入力をリダイレクトした場合は、プログラム自身の出力だけが表示されます。
breakを使うwhile (true)
本体の途中、つまりある処理の後で残りの処理の前が、自然な終了地点になることがあります。ループを while (true) と書き、break で抜けます。
出力:
processing: resize photo
processing: send email
stop signal received
1 job(s) left in the queue
Dequeue は キュー の先頭から次のジョブを取り出し、ループは停止の目印を見た時点で抜けるので、最後のジョブはキューに残ります。終了条件が明確で本体の先頭近くにあるなら、while (true) は悪いコードの兆候ではありません。長い本体のあちこちに break が散らばっていると、そうなります。その場合は名前の付いた bool の条件か、return するメソッドのほうが追いやすくなります。ネストしたループから抜ける方法はbreakとcontinueのページで扱います。
終わらないループ
意図しない無限ループの原因は、次の3つのいずれかです。
本体が条件を変えていない。 カウンターを増やしていないか、別の変数を増やしています。
int i = 0;
while (i < 5)
{
Console.WriteLine(i); // i++ forgotten: prints 0 forever
}
条件の後のセミコロン。 セミコロンがループの本体全体になり、その下のブロックはループの後で実行されます(ループは終わりませんが)。
while (retries < 3); // warning CS0642: Possible mistaken empty statement
{
retries++;
}
目標を飛び越える条件。 変数が一定の幅で動くときに != で判定すると、停止する値を飛び越えることがあります。while (x != 10) x += 3; は0、3、6、9、12と進み、10をそのまま通り過ぎます(何十億ステップ後に int がオーバーフローして一周し、偶然10に着地したときにだけ止まります)。増分が1でないときは、!= ではなく < や > で判定します。同じことは、正確な目標値にめったに一致しない double の値にも当てはまります:while (x != 1.0) x += 0.1; は終わりません。
while、do while、forの選び方
| 状況 | ループ |
|---|---|
| 反復回数がわかっている、またはインデックスを使う | for |
| コレクションのすべての要素 | foreach |
| 条件が成り立つ間繰り返す(0回もありうる) | while |
| 条件が意味を持つ前に本体を1回実行する必要がある | do while |
| 終了判定が本体の途中にある | break を使う while (true) |
どの for ループも while ループに書き換えられ、その逆もできます。選択の基準は、ループの論理を読む人が期待する場所に置けるのはどちらか、です。カウンター、判定、増分がすべてそろっているなら、for はそれらを1行にまとめます。本体の中で変わる状態がループを動かしているなら、while がそれをそのまま表します。
よくある質問
C#のwhileとdo whileの違いは何ですか?
while ループは各反復の前に条件を確認するので、本体が1回も実行されないことがあります。do { } while (condition); ループは各反復の後に確認するので、本体は常に少なくとも1回実行されます。入力を読んでから検証するように、最初の実行で条件が判定する値が生まれる場合は do while を使います。
C#のdo whileループの構文は?
do { body } while (condition); です。閉じかっこの後のセミコロンに注意してください。必須で、省くとコンパイルエラーになります。条件では本体で代入した変数を使えますが、本体の波かっこは別のスコープなので、それらの変数は do の前に宣言しておく必要があります。
C#でwhile (true)はどう書きますか?
while (true) { ... } は、中の何かが break、return、例外で抜け出すまで永遠に繰り返します。1行読んで空なら止めるなど、終了判定が本体の途中にあるときに自然な形です。
C#でユーザーが特定の単語を入力するか入力が終わるまで読み込むには?
入力の終わりで null を返す Console.ReadLine() でループします:string line; while ((line = Console.ReadLine()) != null && line != "done") { ... }。条件の中の代入が、次の行の読み込みと判定を1ステップで行います。
C#のwhileループが終わらないのはなぜですか?
たいていは、本体が条件の判定対象を変えていない(i++ の書き忘れ)か、条件の直後にセミコロンがあるかのどちらかです。while (count < 10); は本体が空のループで、永遠に回り続けます。2つ目のケースについて、コンパイラはCS0642 Possible mistaken empty statement で警告します。