動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

C# の switch式とパターンマッチングの使い方3パターン(.NET Framework 4.7.2 でも使える)

バイブス父さん
現役の業務SE
2026年7月8日15 min read
C# の switch式とパターンマッチングの使い方3パターン(.NET Framework 4.7.2 でも使える)

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つだけ。

  1. csproj に <LangVersion>8.0</LangVersion> を足す
  2. 変数 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式は純粋なコンパイラの構文糖です。コンパイル後は普通の ifswitch の 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 文と並べて見てください。

従来の switch 文を switch 式に書き換える before/after。case/return/break が pattern => value の1行に置き換わる

casereturnbreak が消えて、パターン => 値 の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)); // 不合格
    }
}

実行結果:

when句で点数を4段階に判定した実行結果

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 >= 80when 句を使います。地味だけど、これを知らないとサンプルが動かなくて時間を溶かします。

switch式と従来の switch 文、どっちを使うか

「じゃあ全部 switch式にすればいいの??」というと、そうでもない。使い分けの軸を表にしました。

switch式と従来のswitch文を、値を返す・副作用・網羅性チェック・複数文・C#7.3対応の観点で比較した表

ざっくり言うと、「値を作って返す」なら 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 で経歴詳細を見る


この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について