前回の記事では if 文のパターンマッチングを整理しました。同じパターンは switch 文でも使えます。C# 7.0 で switch にパターンマッチングが導入され、C# 8.0 で switch 式、C# 9.0 でさらに新しいパターンが追加されました。本記事では switch 文(statement)を中心に変遷を整理します。
C# 7.0 以前の switch 文
C# 7.0 より前の switch は、定数値との完全一致しか比較できませんでした。
int code = 2;
switch (code)
{
case 1:
Console.WriteLine("one");
break;
case 2:
Console.WriteLine("two");
break;
default:
Console.WriteLine("other");
break;
}
case に指定できるのは整数・文字列・列挙型などのコンパイル時定数のみで、型チェックや範囲チェックはできませんでした。
C# 7.0 — 型パターン
C# 7.0 から case に型パターンを書けるようになりました。is 演算子と同様に、型チェックと変数束縛を一度に行えます。
object item = 3.14;
switch (item)
{
case int i:
Console.WriteLine($"整数: {i}");
break;
case double d:
Console.WriteLine($"浮動小数点: {d}");
break;
case string s:
Console.WriteLine($"文字列: {s}");
break;
default:
Console.WriteLine("その他");
break;
}
// → 浮動小数点: 3.14
マッチした case の変数(i、d、s)はそのブロック内で使えます。
null のマッチング
型パターンは null にマッチしません。null を明示的に処理したい場合は case null: を追加します。
object item = null;
switch (item)
{
case null:
Console.WriteLine("null です");
break;
case string s:
Console.WriteLine($"文字列: {s}");
break;
}
case null: は型パターンより前に書いても後に書いても動作しますが、意図を明確にするため先頭近くに置くのが一般的です。
C# 7.0 — when 句(ガード条件)
case に when 句を追加することで、パターンに一致した後さらに条件を絞り込めます。
object item = -5;
switch (item)
{
case int i when i > 0:
Console.WriteLine($"正の整数: {i}");
break;
case int i when i < 0:
Console.WriteLine($"負の整数: {i}");
break;
case int i:
Console.WriteLine("ゼロ");
break;
default:
Console.WriteLine("整数ではない");
break;
}
// → 負の整数: -5
case は上から順に評価されます。最初にマッチした case が実行されるため、より具体的な条件を先に書きます。
型パターンと when 句の組み合わせ例
object shape = new Rectangle { Width = 4, Height = 6 };
switch (shape)
{
case Circle c when c.Radius == 0:
Console.WriteLine("点(半径 0 の円)");
break;
case Circle c:
Console.WriteLine($"円: 半径 {c.Radius}");
break;
case Rectangle r when r.Width == r.Height:
Console.WriteLine($"正方形: 辺 {r.Width}");
break;
case Rectangle r:
Console.WriteLine($"長方形: {r.Width} x {r.Height}");
break;
default:
Console.WriteLine("不明な図形");
break;
}
// → 長方形: 4 x 6
継承関係があるクラス群の処理分岐を、型キャストなしに書けるのが大きなメリットです。
C# 7.0 — case の順序と網羅性
型パターンを使う case はコンパイラが順序を検証します。より一般的なパターンが先にある場合、後の case に到達できないとコンパイルエラーになります。
switch (item)
{
case object o: // object はすべてにマッチする
Console.WriteLine(o);
break;
case string s: // ここには絶対に到達できない → コンパイルエラー
Console.WriteLine(s);
break;
}
case object o: がすべての非 null 値にマッチするため、case string s: は到達不能と判定されます。
C# 9.0 — 関係パターン・論理パターン
C# 9.0 で追加された関係パターン(>, >=, <, <=)と論理パターン(not, and, or)は switch 文の case でも使えます。when 句なしで範囲条件を直接 case に書けるようになりました。
関係パターン
int score = 75;
switch (score)
{
case >= 90:
Console.WriteLine("優");
break;
case >= 70:
Console.WriteLine("良");
break;
case >= 50:
Console.WriteLine("可");
break;
default:
Console.WriteLine("不可");
break;
}
// → 良
C# 7.0 の when 句で書いていた case int i when i >= 90: が、case >= 90: とシンプルになります。
and パターンで閉区間を表現
int value = 15;
switch (value)
{
case >= 1 and <= 10:
Console.WriteLine("1〜10");
break;
case >= 11 and <= 20:
Console.WriteLine("11〜20");
break;
default:
Console.WriteLine("範囲外");
break;
}
// → 11〜20
or パターンで複数値をまとめる
string day = "Saturday";
switch (day)
{
case "Saturday" or "Sunday":
Console.WriteLine("週末");
break;
default:
Console.WriteLine("平日");
break;
}
// → 週末
定数パターンの or は複数の case ラベルを並べる書き方と同義ですが、1 行にまとめられます。
not パターン
object item = 42;
switch (item)
{
case not null:
Console.WriteLine($"null ではない: {item}");
break;
default:
Console.WriteLine("null");
break;
}
switch 式(C# 8.0)との使い分け
C# 8.0 では switch 式が導入され、各アームが値を返せるようになりました。
// switch 式(C# 8.0)
string result = score switch
{
>= 90 => "優",
>= 70 => "良",
>= 50 => "可",
_ => "不可",
};
switch 文と switch 式の使い分けの目安は以下のとおりです。
| 使い分け | 推奨 |
|---|---|
| 値を返したい・代入したい | switch 式 |
| 複数の処理をまとめて実行したい | switch 文 |
break / return が各 case で異なる |
switch 文 |
まとめ
| バージョン | 追加された機能 | 例 |
|---|---|---|
| C# 7.0 | 型パターン | case string s: |
| C# 7.0 | 定数パターン | case null: |
| C# 7.0 | when 句 |
case int i when i > 0: |
| C# 9.0 | 関係パターン | case >= 90: |
| C# 9.0 | and パターン |
case >= 1 and <= 10: |
| C# 9.0 | or パターン |
case "Sat" or "Sun": |
| C# 9.0 | not パターン |
case not null: |
switch 文のパターンマッチングを使うと、複数の型や条件に応じた分岐を型安全かつ読みやすく書けます。値を返すだけの分岐であれば switch 式をあわせて検討してみてください。