C# の switch式とパターンマッチングの使い方3パターン(.NET Framework 4.7.2 でも使える)
みなさんこんにちは!ヒロポンです!
ステータス値を文字列に変換するだけの処理で、if-else が20行、30行と積み上がっていく画面。ありませんか??
私も業務系の現場で引き継いだことがあります。注文ステータスの変換メソッドが if (status == 0) ... else if (status == 1) ... で40行くらいになってるやつ。
読むだけで疲れます。
この「値で分岐して値を返すだけ」の処理を、C# 8 の switch式 で書くと一気に短くなる。この記事では C# switch式 の使い方 を、レガシーな .NET Framework 4.7.2 の現場でもそのまま動く前提で、コピペで試せる3パターンに絞って紹介します。
先に一番大事なところだけ。「うちは Framework だから新しい構文は無理」と思ってる人、それ半分は誤解です。
結論: switch式は「値を返す分岐」を1行で書ける。まず LangVersion を 8.0 に
switch式(switch expression)とは、値を受け取って別の値を返す分岐を、式として1つの結果に評価する C# 8 の構文です。従来の switch 文が「処理を分岐させる命令」だったのに対して、switch式は「分岐した結果の値そのもの」になります。
やることは2つだけ。
- csproj に
<LangVersion>8.0</LangVersion>を足す 変数 switch { パターン => 値, ... }の形で書く
これで .NET Framework 4.7.2 でも switch式が使えます。ランタイム(4.7.2)はそのまま、C# コンパイラのバージョンだけ上げるイメージ。
<PropertyGroup>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
なぜ .NET Framework 4.7.2 でも switch式が使えるのか
ここ、業務SEが最初に不安になるところ。先に潰しておきます。
switch式は純粋なコンパイラの構文糖です。コンパイル後は普通の if や switch の IL に落ちるだけで、新しいランタイムのAPIを呼んでるわけじゃない。
だから .NET Framework 4.7.2 のままでも、Visual Studio と C# コンパイラが C# 8 に対応していれば動きます。
注意点が1つ。C# 8 の機能でも「範囲(Range)」や「インデックス(Index)」のように、ランタイム側の型が必要な機能は 4.7.2 では動きません。
でも switch式とパターンマッチングは構文だけの話。この制約には引っかかりません。ここは安心して大丈夫です。
VS 2019 なら C# 8 に対応してるので、LangVersion を上げるだけ。VS 2017 だと C# 7.3 止まりなので、そこは環境を確認してください。
最短対処: コピペで動く switch式 3パターン
実際の書き方を3つ見ていきます。どれも dotnet で普通に動くコードです。
パターン1: 値でマッチする基本形
一番よく使うのが、int や文字列の値で分岐して別の値を返す形。まず従来の switch 文と並べて見てください。

case と return と break が消えて、パターン => 値 の1行になりました。
末尾の _ は破棄パターンで、従来の default にあたります。「それ以外は全部これ」の受け皿ですね。
戻り値がそのまま式になるので、こんな感じでメソッドを式本体(=>)でスッと書けます。
パターン2: 型パターン(is 型 変数)
次は、渡ってきたオブジェクトの型で分岐するパターン。is 型 変数 の形をそのままアームに書けます。支払い方法ごとに手数料を計算する例で見てみます。
using System;
abstract class Payment { public decimal Amount { get; set; } }
class CreditCard : Payment { }
class BankTransfer : Payment { }
class Cash : Payment { }
class Program
{
static decimal CalcFee(Payment payment) => payment switch
{
CreditCard c => c.Amount * 0.03m,
BankTransfer b => b.Amount * 0.01m,
Cash _ => 0m,
_ => throw new ArgumentException("未対応の支払い方法"),
};
static void Main()
{
Console.WriteLine(CalcFee(new CreditCard { Amount = 10000m })); // 300.00
Console.WriteLine(CalcFee(new BankTransfer { Amount = 10000m })); // 100.00
Console.WriteLine(CalcFee(new Cash { Amount = 10000m })); // 0
}
}
実行結果(decimal なので手数料はスケールを保って 300.00 と出ます):

CreditCard c と書くと「CreditCard 型なら、その値を c として受け取る」という意味になります。c.Amount みたいにキャストなしでプロパティを触れるのが型パターンのおいしいところ。
昔なら if (payment is CreditCard) { var c = (CreditCard)payment; ... } と二度手間だったのが、いい感じに1行になります!!
パターン3: when 句で条件を足す
「型は同じだけど値の範囲で分けたい」ときは when 句を足します。点数を判定する例です。
using System;
class Program
{
static string Classify(int score) => score switch
{
var n when n < 0 => "エラー値",
var n when n >= 80 => "合格",
var n when n >= 60 => "再チェック",
_ => "不合格",
};
static void Main()
{
Console.WriteLine(Classify(-5)); // エラー値
Console.WriteLine(Classify(90)); // 合格
Console.WriteLine(Classify(65)); // 再チェック
Console.WriteLine(Classify(30)); // 不合格
}
}
実行結果:

var n when 条件 で「値を n に束縛しつつ、条件を満たしたらこのアーム」という書き方になります。
上から順に評価されるので、書く順番が結果を左右するのがポイント。n >= 60 を先に書くと 90点も「再チェック」になっちゃう。厳しい条件から並べます。
ハマりポイント: 知らないと本番で例外が飛ぶやつ
ここからが本題かもしれません。switch式は書き味がいい分、油断すると本番で実行時例外を吐きます。私が踏んだやつを中心に3つ。
① 全パターンを網羅してないと CS8509 警告が出る
switch式は「全部の値をちゃんと処理した??」とコンパイラがチェックしてきます。網羅できてないと CS8509(可能なすべての値を処理していない) の警告が出ます。
enum Status { Todo, Doing, Done }
// Done を書き忘れ → CS8509 警告
string Label(Status s) => s switch
{
Status.Todo => "未着手",
Status.Doing => "進行中",
};
これ、_ => を足せば警告は消えます。でも消し方には罠があって、_ => で握りつぶすと 後から enum に値を追加したときに「網羅漏れ」に気づけなくなります。
// _ を置けば警告は消える。ただし enum 追加時の抜けに気づけない諸刃の剣
string Label(Status s) => s switch
{
Status.Todo => "未着手",
Status.Doing => "進行中",
Status.Done => "完了",
_ => "不明",
};
enum を全部明示的に書いて _ を置かない、という選択もアリ。そうすると値を追加したとき CS8509 が「ここ追加漏れてるよ」と教えてくれる。
警告をあえて残す設計、という判断ですね。
② null は型パターンに当たらず実行時例外が飛ぶ(4.7.2 は InvalidOperationException)
これが一番やられました。型パターンは null にマッチしません。参照型を switch式にかけるとき、null を受け止めるアームが無いと実行時に例外が飛びます。
ここは版で例外の型が変わるので要注意。この記事の前提の .NET Framework 4.7.2 では InvalidOperationException です。.NET Core 3.0 以降だと、その派生クラスの SwitchExpressionException に変わります(Framework の BCL には無いクラスです)。
string ToShortName(string dept) => dept switch
{
"営業部" => "営業",
"経理部" => "経理",
_ => dept, // ← この _ が無いと、null や未知の値で例外
};
_ => があれば null も _ に落ちるので大丈夫。でも型パターンだけを並べて _ を書き忘れると、null が来た瞬間にどのアームにも当たらず落ちます。DB から取った値が null だった、みたいな現場でよくあるやつ。
私はこれ、テスト環境では動いてたのに本番で特定の1レコードだけ null を持ってて例外。というパターンで小一時間溶かしました。
参照型を扱う switch式は、null => か _ => を必ず置く。これ鉄則です。
③ C# 8 では > 80 の関係パターンは使えない(when で書く)
もう1つ、Framework 現場ならではの落とし穴。ネットで見かける switch式のサンプルには、こういう書き方があります。
// これは C# 9 以降。C# 8 ではコンパイルエラー
static string Classify(int score) => score switch
{
< 0 => "エラー値",
>= 80 => "合格",
_ => "その他",
};
< 0 や >= 80 という関係パターン(relational pattern)は C# 9 からです。.NET Framework 4.7.2 + C# 8 の環境だとコンパイルが通りません。「サンプル通りに書いたのに動かない??」ってなるやつ。
C# 8 で範囲条件をやりたいときは、パターン3で書いたように var n when n >= 80 の when 句を使います。地味だけど、これを知らないとサンプルが動かなくて時間を溶かします。
switch式と従来の switch 文、どっちを使うか
「じゃあ全部 switch式にすればいいの??」というと、そうでもない。使い分けの軸を表にしました。

ざっくり言うと、「値を作って返す」なら switch式、「何かを実行する(副作用がある)」なら従来の switch 文です。
たとえば「ステータスに応じて DB を更新してログを出してメール送る」みたいな、1ケースで複数の処理を走らせる分岐。これは無理に式にせず switch 文のまま書いたほうが読みやすい。
式は「1つの値に評価される」のが本質なので、副作用の塊を押し込むと逆に読みにくくなります。
現場メモ: if-else の積み上げを式に寄せると値の組み立てが読める
冒頭で書いた、注文ステータス変換の40行 if-else。あれを switch式に寄せたら、10行ちょいになりました!!
行数もですが、それ以上に「この変数は結局どの値になるの?」が一目で追えるようになったのが大きかった。
if-else だと、途中で別の変数をいじってたり return が散らばってたりして、「最終的に何が返るか」を頭の中で組み立てないといけない。switch式は 入力 => 出力 が縦に並ぶので、入力と出力の対応表を読む感覚になります。
業務系の「区分コードを表示名に変換」みたいな処理とは相性が抜群で、良い感じに読めます。
一方で、なんでもかんでも式にすると when 句が長くなってネストが深くなることもある。条件が入り組んできたら、素直にメソッドに切り出すか従来の switch 文に戻す。
式は万能薬じゃない、くらいの温度感がちょうどいいです。
まとめ
C# の switch式、要点だけ振り返ります。
- switch式は構文糖なので、
LangVersionを 8.0 にすれば .NET Framework 4.7.2 でも動く - 3パターン押さえれば実務は回る(値マッチ / 型パターン /
when句) - CS8509 警告は網羅漏れのサイン。
_で握りつぶすか、あえて残して追加漏れ検知に使うかは設計判断 - 参照型 + null は型パターンに当たらず例外。
null =>か_ =>を必ず置く - 範囲条件の
< 0>= 80は C# 9。C# 8 ではwhen句で書く - 副作用のある分岐は無理に式にせず従来の
switch文で
分岐を「命令の並び」じゃなく「値の対応表」として書けると、業務系のコードはだいぶ読みやすくなります。まずは既存の短い変換メソッド1個から寄せてみてください。
よくある質問
Q1. switch式は .NET Framework 4.7.2 でも本当に使えますか?
使えます。switch式はコンパイラが普通の分岐の IL に変換するだけの構文糖で、ランタイムの新機能は使いません。csproj に <LangVersion>8.0</LangVersion> を足せば 4.7.2 でも動きます。ただし VS 2017 は C# 7.3 止まりなので、VS 2019 以上を使ってください。
Q2. CS8509 の警告は消したほうがいいですか?
ケースバイケースです。_ => のアームを足せば消えますが、enum に値を追加したときの網羅漏れに気づけなくなります。逆に _ を置かず全ケースを明示しておくと、値を追加したとき CS8509 が抜けを教えてくれます。将来の追加を検知したいなら、あえて警告を残す設計もアリです。
Q3. switch式に null を渡すと例外になるのはなぜですか?
型パターン(CreditCard c など)は null にマッチしない仕様だからです。null 用のアーム(null => か _ =>)が無いと、どのアームにも当たらず実行時例外が飛びます(.NET Framework 4.7.2 なら InvalidOperationException、.NET Core 3.0 以降は SwitchExpressionException)。DB から取った参照型は null の可能性があるので、必ず受け皿のアームを置いてください。
Q4. 従来の switch 文はもう使わなくていいですか?
いいえ、使い分けです。値を組み立てて返すなら switch式、DB更新やログ出力など副作用のある処理を分岐させるなら従来の switch 文が読みやすいです。1ケースで複数の文を書きたい処理も、文のまま残したほうがスッキリします。
次に読むべき記事





以上!
同じ罠でハマった人いたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務 SE 7 年(正社員 2 / フリーランス 5)。現職は SEO 直轄部の AI アドバイザー兼 PL、副業で中小 SIer の CTO。SES 複数社・フリーランスエージェント複数経由の経験ベースで「業務 SE 視点」の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524(日々の現場メモ更新中)
📝 About Me で経歴詳細を見る




