C#で「ある条件を満たしたときだけ処理する」「値に応じて処理を切り替える」といったロジックを書くとき、必ず使うのが if文 と switch文 です。
基本構文は数分で覚えられますが、「複合条件をどう整理するか」「switch文とif文をどう使い分けるか」「C# 8以降のswitch式とどう向き合うか」となると、迷いやすいポイントが増えてきます。本記事では基本から実務で役立つイディオムまでまとめて解説します。
if文の基本構文
if 文はもっともシンプルな条件分岐です。条件式が true のときだけブロック内の処理が実行されます。
int score = 85;
if (score >= 80)
{
Console.WriteLine("合格です");
}
else で「そうでなければ」を追加する
else ブロックは、if の条件が false のときに実行されます。
int score = 65;
if (score >= 80)
{
Console.WriteLine("合格です");
}
else
{
Console.WriteLine("不合格です");
}
else if で複数の条件を連鎖させる
3通り以上に分岐させたいときは else if を使います。上から順に評価され、最初に true になった条件のブロックだけが実行されます。
int score = 75;
if (score >= 90)
{
Console.WriteLine("優秀です");
}
else if (score >= 70)
{
Console.WriteLine("合格です");
}
else if (score >= 50)
{
Console.WriteLine("追試対象です");
}
else
{
Console.WriteLine("不合格です");
}
論理演算子で複合条件を書く
複数の条件を組み合わせるときは以下の論理演算子を使います。
| 演算子 | 意味 | 動作 |
|---|---|---|
&& |
AND(かつ) | 両方が true のとき true |
|| |
OR(または) | どちらか一方が true のとき true |
! |
NOT(否定) | true ↔ false を反転 |
int age = 25;
bool hasMembership = true;
// AND: 両方の条件を満たす場合
if (age >= 18 && hasMembership)
{
Console.WriteLine("ご利用いただけます");
}
// OR: どちらかを満たす場合
if (age < 13 || age >= 65)
{
Console.WriteLine("割引対象です");
}
// NOT: 条件の否定
if (!hasMembership)
{
Console.WriteLine("会員登録が必要です");
}
&& は左辺が false なら右辺を評価しません。|| は左辺が true なら右辺を評価しません。これを利用して null チェックと値チェックを安全に組み合わせられます。if (obj != null && obj.Value > 0) — obj が null なら右辺は評価されない
三項演算子(条件式)
「条件によって異なる値を変数に代入したい」だけのシンプルなケースは、三項演算子( ? : )を使うとコードが短くなります。
int score = 75; // 書式: 条件式 ? true のときの値 : false のときの値 string result = score >= 70 ? "合格" : "不合格"; Console.WriteLine(result); // 合格
// if文で書いた場合
string label1;
if (score >= 70)
label1 = "合格";
else
label1 = "不合格";
// 三項演算子で書いた場合(1行)
string label2 = score >= 70 ? "合格" : "不合格";
? : ? :)は読みにくくなりやすいため、2段階以上の条件は else if か switch 式を使う方が保守性が上がります。
null チェックのイディオム
C# では null を含む条件判定に便利な構文があります。
string? name = GetName(); // null になり得る変数
// 従来の書き方
if (name != null)
{
Console.WriteLine(name.Length);
}
// C# 9以降: is null / is not null(推奨)
if (name is not null)
{
Console.WriteLine(name.Length);
}
// null チェックと値の利用を同時に(パターンマッチング)
if (name is string s && s.Length > 0)
{
Console.WriteLine($"名前: {s}");
}
is not null は != null と同じ動作ですが、演算子オーバーロードの影響を受けないという利点があります。C# 9以降はこちらの記法が推奨されています。
ガード節パターン(早期リターン)
メソッドの冒頭で「この条件を満たさないなら即座に返す」という書き方を ガード節(Guard Clause) と呼びます。ネストが深くなるのを防ぎ、本来の処理を目立たせることができます。
void ProcessOrder(Order? order)
{
if (order != null)
{
if (order.Items.Count > 0)
{
if (order.TotalPrice > 0)
{
// ようやく本来の処理
Console.WriteLine($"注文処理: {order.TotalPrice}円");
}
}
}
}
void ProcessOrder(Order? order)
{
if (order is null) return; // ガード: null チェック
if (order.Items.Count == 0) return; // ガード: 空チェック
if (order.TotalPrice <= 0) return; // ガード: 金額チェック
// 本来の処理だけが残る
Console.WriteLine($"注文処理: {order.TotalPrice}円");
}
switch文の基本構文
switch 文は、1つの変数や式を複数の値と照合して分岐させるときに使います。else if の連鎖より意図が明確になります。
string grade = "B";
switch (grade)
{
case "A":
Console.WriteLine("優秀です");
break;
case "B":
Console.WriteLine("良好です");
break;
case "C":
Console.WriteLine("合格です");
break;
default:
Console.WriteLine("不合格です");
break;
}
case の末尾には必ず break(または return/throw)が必要です。C# では C/Java と異なり、フォールスルー(次のcaseへ自動流入)はコンパイルエラーになります。
複数のcaseを同じ処理にまとめる
同じ処理をする複数の値は case を並べてまとめられます。
int day = 6;
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
Console.WriteLine("平日です");
break;
case 6:
case 7:
Console.WriteLine("週末です");
break;
default:
Console.WriteLine("無効な値です");
break;
}
when句で条件を追加する
C# 7以降、case に when キーワードで追加条件を付けられます。同じ型・同じ値でも条件を細かく絞れます。
int score = 85;
string category = "math";
switch (score)
{
case int s when s >= 90 && category == "math":
Console.WriteLine("数学トップクラス");
break;
case int s when s >= 80:
Console.WriteLine("優良");
break;
case int s when s >= 60:
Console.WriteLine("合格");
break;
default:
Console.WriteLine("要フォロー");
break;
}
switch式(C# 8.0以降)の基本
C# 8.0 で導入された switch式 は、switch文をより簡潔に書けるようにした構文です。各アームが「条件 => 結果の値」の形式になり、戻り値を直接返すことができます。
// switch文(従来)
string GetDayName_Old(int day)
{
switch (day)
{
case 1: return "月曜日";
case 2: return "火曜日";
case 3: return "水曜日";
case 4: return "木曜日";
case 5: return "金曜日";
case 6: return "土曜日";
case 7: return "日曜日";
default: return "不明";
}
}
// switch式(C# 8以降)
string GetDayName(int day) => day switch
{
1 => "月曜日",
2 => "火曜日",
3 => "水曜日",
4 => "木曜日",
5 => "金曜日",
6 => "土曜日",
7 => "日曜日",
_ => "不明" // defaultの代わりに _ (破棄パターン)
};
int month = 4;
// 変数に直接代入できる
int daysInMonth = month switch
{
1 or 3 or 5 or 7 or 8 or 10 or 12 => 31,
4 or 6 or 9 or 11 => 30,
2 => 28, // うるう年は別途処理
_ => throw new ArgumentOutOfRangeException(nameof(month))
};
Console.WriteLine(daysInMonth); // 30
_(アンダースコア)はどんな値にもマッチする 破棄パターン で、switch文の default に相当します。型・プロパティ・論理パターンを使ったより高度なパターンマッチングは、パターンマッチングの基礎・switch式の応用とswitch式でのパターンマッチング活用を参照してください。
if文 vs switch文・switch式の使い分け
| 構文 | よく合うケース | 具体例 |
|---|---|---|
| if文 | 範囲条件(>= / <=)・複合条件(&& / ||)・型による分岐 | 「スコアが80以上」「nullでかつ空でない」 |
| switch文 | 固定値への一致・複数ケースの整理・when句で補足条件 | 「曜日ごとの処理」「ステータスコードごとの処理」 |
| switch式 | 値を返す分岐・1対1の変換マップ・シンプルなパターンマッチング | 「数値 → ラベル変換」「コマンド → 処理の割り当て」 |
// ① 範囲条件 → if文が自然
int temp = 25;
if (temp >= 30)
Console.WriteLine("猛暑です");
else if (temp >= 25)
Console.WriteLine("夏日です");
else if (temp >= 15)
Console.WriteLine("過ごしやすいです");
else
Console.WriteLine("寒いです");
// ② 固定値の一致 → switch文が自然
string status = "ACTIVE";
switch (status)
{
case "ACTIVE": Console.WriteLine("有効"); break;
case "INACTIVE": Console.WriteLine("無効"); break;
case "PENDING": Console.WriteLine("保留中"); break;
default: Console.WriteLine("不明"); break;
}
// ③ 値を返す変換 → switch式が自然
string GetStatusLabel(string code) => code switch
{
"ACTIVE" => "有効",
"INACTIVE" => "無効",
"PENDING" => "保留中",
_ => "不明"
};
よくある落とし穴と注意点
= と == の混同
条件式の中で =(代入)と ==(比較)を書き間違えることがあります。C# では if (x = 5) はコンパイルエラーになるため早期に気づけますが、書き慣れていない段階では戸惑いやすいポイントです。条件比較には必ず == を使います。
switch文のbreak忘れ
C# では break を省略するとコンパイルエラーになります。ただし case ブロックに処理を何も書かない場合(空のフォールスルー)は次の case へ流れます。複数の case を同じ処理に使うときは「何も書かない空のcase」を並べることが明示的な意図になります。
else ifの条件の順序ミス
else if を使うとき、広い条件を先に書いてしまうと、それ以降の条件が永遠に評価されません。たとえば score >= 50 を score >= 80 より先に書くと、80点以上のケースが50点以上の分岐で捕捉されてしまいます。より厳しい(狭い)条件を上に、より緩い(広い)条件を下に書くのが基本です。
switch式での網羅性チェック(列挙型)
switch式が列挙型(enum)を対象とする場合、_(破棄パターン)を省略するとコンパイラが「全ケースを網羅していない」と警告します。この警告はバグを未然に防ぐ有益なシグナルです。列挙型にメンバーを追加したとき、switch式も更新が必要になることを知らせてくれます。意図的に default 動作が不要な場合は throw new InvalidEnumArgumentException() を書いておくと意図が明確です。
複雑な条件式は変数や関数に切り出す
条件式が長くなると可読性が落ちます。if (user != null && user.IsActive && user.Role == "Admin" && !user.IsLocked) のような条件は、bool isAuthorized = ... のように意味のある名前の変数に切り出すか、プライベートメソッド(IsAuthorizedAdmin(user))に分離するのが保守性の高い書き方です。
よくある質問
_(破棄パターン)はswitch式専用の「どんな値にもマッチする」パターンで、switch文の default に相当します。switch文では引き続き default: を使い、switch式では _ => を使います。両者を混用することはできません。if (obj is string s) は型確認とキャストを同時に行えます。switch式での型パターン・プロパティパターン・論理パターン(and/or/not)の詳細はパターンマッチングの基礎・switch式の応用を参照してください。まとめ
if文とswitch文のポイントを整理します。
| 構文 | 用途 | 注意点 |
|---|---|---|
| if文 | あらゆる条件(範囲・複合・null チェック)に対応 | 条件式に真偽値を返す任意の式が書ける |
| else if | 3通り以上の分岐。条件は上(厳しい)→下(緩い)の順に書く | 条件の順序ミスに注意 |
| 三項演算子 | 1段階の2択をコンパクトに書く | 2段階以上のネストは禁止 |
| ガード節 | メソッド冒頭で例外条件を早期return・ネストを減らす | コードの可読性が大きく向上する |
| switch文 | 固定値との一致・複数caseのまとめ・when句で補足条件 | breakを各caseに忘れずに |
| switch式 | 値を返す分岐・変換マップ。C# 8以降 | _ が default の代わり。列挙型では全ケース網羅を確認 |
基本的な条件分岐を正確に書けることは、C#プログラミングの土台です。まずはif文とswitch文の使い分けを身につけ、余裕ができたらswitch式のパターンマッチングに挑戦してみてください。
より高度なパターンマッチング(型パターン・プロパティパターン・論理パターン)はパターンマッチングの基礎・switch式の応用とswitch式でのパターンマッチング活用で詳しく解説しています。

