C#のswitchステートメントは、単なる定数比較を超えて、型チェック、条件付きフィルタリング、および複雑なオブジェクト構造へのパターンマッチングをサポートします。C# 7以降で導入されたパターンマッチング機能により、switchは型安全かつ表現力豊かな分岐機構へと進化しました。
パターンベースのcaseラベル
caseラベルは「パターン」で構成され、以下の種類が混在可能です:
- 定数パターン(例:
case 42:) - 型パターン(例:
case Rectangle r:) - 条件付き型パターン(
when節付き) - ヌルチェックパターン(
case null:)
特にwhen節は、パターン一致後に追加の論理評価を行うための重要な構文です:
case Rectangle rect when rect.Width > 10 && rect.Height > 5:
Console.WriteLine($"大型矩形: {rect.Width}×{rect.Height}");
break;
この例では、rectはパターン変数であり、switch対象の値から型変換・代入が行われます。そのスコープは、対応するbreak(またはreturn・throwなど制御フロー終了点)までに限定されます。
複数のcaseラベルの連鎖
実行可能なコードを挟まない限り、複数のcaseラベルを同一処理ブロックに結合できます:
int status = 2;
switch (status)
{
case 1:
case 2:
case 3:
Console.WriteLine("有効な状態コード");
break;
default:
Console.WriteLine("不明な状態");
break;
}
この構文は、列挙型や整数コードの範囲判定に簡潔に使えます。
制約付きパターンの競合回避
同一switch内に互いに包含関係にあるパターン(例:case Shape s: と case Rectangle r:)を並べると、コンパイラは曖昧性を検出しエラーを報告します。これは、ランタイムでの予測不可能なマッチングを防ぐための安全機構です。
完全なカバレッジとデフォルト処理
すべてのケースを網羅する必要はありませんが、default節またはcase null:のような明示的な未定義ケースのハンドリングは、例外を防ぐ上で重要です。また、各ブランチは必ず制御フローを明確に終了させる必要があります(break、return、throw、goto caseなど)。
実装例:形状オブジェクトの動的処理
abstract record Geometry();
record Rectangle(double Width, double Height) : Geometry;
record Ellipse(double Major, double Minor) : Geometry;
record Triangle(double Base, double Altitude) : Geometry;
void ProcessGeometries(IEnumerable<Geometry> items)
{
foreach (var item in items)
{
switch (item)
{
case Rectangle r when r.Width == r.Height:
Console.WriteLine($"正方形: {r.Width}");
break;
case Rectangle r:
Console.WriteLine($"長方形: {r.Width}×{r.Height}");
break;
case Ellipse e when e.Major == e.Minor:
Console.WriteLine($"円: 半径 {e.Major}");
break;
case Ellipse e:
Console.WriteLine($"楕円: 長軸 {e.Major}, 短軸 {e.Minor}");
break;
case Triangle t:
Console.WriteLine($"三角形: 底辺 {t.Base}, 高さ {t.Altitude}");
break;
case null:
Console.WriteLine("null値が検出されました");
break;
default:
throw new NotSupportedException($"未サポートの幾何形状: {item.GetType().Name}");
}
}
}
この例では、レコード型とwhen節を組み合わせて、形状の特性(例:正方形/長方形の区別)を自然に表現しています。パターン変数は型安全性を保ちながら、即座にプロパティアクセスが可能です。