C#の条件分岐と繰り返し — if・switch式・foreach
この章の目次開く
条件分岐と繰り返しは、どの言語でも見た目が似ています。C#で押さえるべきは、switch が「式」としても書けることと、foreach が何を相手にしているのかの2点です。
if文と三項演算子
if の書き方はJavaScriptとほぼ同じですが、条件式は必ず bool である必要があります。
int score = 82;
if (score >= 80)
{
Console.WriteLine("合格");
}
else if (score >= 60)
{
Console.WriteLine("再テスト");
}
else
{
Console.WriteLine("不合格");
}短い分岐は三項演算子で書けます。
string result = score >= 80 ? "合格" : "不合格";switch文とswitch式
C#の switch には、昔からある文(statement)と、C# 8以降で使える式(expression)の2つの形があります。
// switch文
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("処理待ち");
break;
case OrderStatus.Shipped:
Console.WriteLine("発送済み");
break;
default:
Console.WriteLine("不明");
break;
}// switch式(値を返す)
string label = status switch
{
OrderStatus.Pending => "処理待ち",
OrderStatus.Shipped => "発送済み",
_ => "不明"
};| switch文 | switch式 | |
|---|---|---|
| 目的 | 処理を分岐する | 値を求める |
break | 必要 | 不要 |
| 網羅性のチェック | されない | されない(ただし _ が無いと実行時例外) |
| 書ける場所 | 文として | 式として(代入や return の右辺) |
_ は「それ以外すべて」を表すパターンで、default に相当します。switch式で _ を書かず、どのパターンにも一致しなかった場合は実行時に例外が発生します。
学習者switch文はJavaScriptと同じでしたが、break を書き忘れたらどうなりますか?
C#ではコンパイルエラーになります。JavaScriptのように次の case へ処理が流れ落ちる(フォールスルー)ことは、原則として許されていません。書き忘れによるバグが起きない設計になっています。
条件付きのswitch式
when を使うと、パターンに条件を付けられます。
string rank = score switch
{
>= 90 => "S",
>= 80 => "A",
>= 60 => "B",
_ => "C"
};このような書き方はパターンマッチングと呼ばれ、型による分岐も可能です。詳しくは C#のモダン構文 で扱います。
ループの使い分け
| 構文 | 向いている場面 |
|---|---|
foreach | コレクションの全要素を順に処理する(最も多い) |
for | 回数が決まっている、インデックスが必要 |
while | 終了条件が回数で決まらない |
do-while | 最低1回は実行したい |
var names = new List<string> { "田中", "鈴木", "佐藤" };
foreach (var name in names)
{
Console.WriteLine(name);
}
for (int i = 0; i < names.Count; i++)
{
Console.WriteLine($"{i + 1}人目: {names[i]}");
}
int count = 0;
while (count < 3)
{
count++;
}
foreachが相手にしているもの
foreach は、IEnumerable<T> を実装している型なら何でも回せます。配列も List<T> も Dictionary<TKey, TValue> も、LINQの結果も同じように扱えるのはこのためです。
foreach (var n in Enumerable.Range(1, 5))
{
Console.Write(n); // 12345
}構文: Enumerable.Range(start, count)
| 引数 | 渡せるもの | 説明 |
|---|---|---|
start(第1引数) | int | 最初の値 |
count(第2引数) | int | 生成する個数(負の値は例外) |
戻り値: 連続した整数を順に返す IEnumerable<int>
この IEnumerable<T> という共通の枠組みが、C#のコレクション と C#のLINQ入門 の土台になります。
break と continue
foreach (var n in numbers)
{
if (n < 0) continue; // 次の要素へ
if (n > 100) break; // ループを抜ける
Console.WriteLine(n);
}多重ループの内側で break しても、抜けられるのは内側のループだけです。外まで抜けたい場合は、フラグを使うかメソッドに切り出して return します。
よくあるハマりどころ
ループ中にコレクションを変更する
foreach (var user in users)
{
if (user.IsInactive)
{
users.Remove(user); // InvalidOperationException
}
}foreach の実行中に、対象のコレクションへ要素を追加・削除すると実行時例外になります。削除したい場合は RemoveAll を使うか、新しいリストを作ります。
users.RemoveAll(u => u.IsInactive);
先生「ループしながら消す」は各言語で共通のハマりどころだけど、C#は黙って壊れずに例外で止めてくれるぶん親切なんだ。
ループ変数のスコープを勘違いする
for の中で宣言した変数は、ループの外からは見えません。結果を外で使いたい場合は、ループの前に宣言します。
条件式にnullを渡してしまう
bool?(null許容のbool)は、そのままでは if の条件に書けません。if (flag == true) のように明示的に比較するか、GetValueOrDefault() を使います。null許容型については C#のnull安全 で扱います。
ちゃんと使うためのポイント
- 条件式は必ず
bool。数値やnullを真偽値として扱えない - 値を決めたいときは
switch式、処理を分けたいときはswitch文 - switch文のフォールスルーは禁止されており、
break忘れはコンパイルエラー - 全要素を回すなら
foreach、インデックスが必要ならfor foreachの対象はIEnumerable<T>。この共通枠組みがLINQにつながる- ループ中にコレクションを変更しない。削除は
RemoveAllを使う
次の章では、処理をまとめる単位であるメソッドを扱います。引数の渡し方(ref / out / 省略可能引数)まで踏み込みます。