C#における条件分岐の代表格であるswitch文。

その中でも、どのcaseにも当てはまらない場合の処理を規定するdefaultラベルは、プログラムの堅牢性を高めるために極めて重要な役割を果たします。

特に近年、C#はバージョンアップを重ねるごとに「switch式」や「パターンマッチング」といった強力な機能を導入してきました。

本記事では、従来のswitch文におけるdefaultの書き方はもちろん、最新のC#におけるswitch式での「_(アンダースコア)」との違い、さらには実務で役立つ網羅性のチェックや例外処理との組み合わせについて、テクニカルライターの視点で詳しく解説します。

C#のswitch文におけるdefaultの基本

switch文は、一つの変数や式の値に応じて複数の処理を分岐させるための構文です。

if-else ifを並べるよりも可読性が高く、多くの選択肢がある場合に重宝されます。

その中でdefaultは、「提示されたcaseのいずれにも一致しなかった場合に実行されるセクション」を定義します。

基本的な構文と書き方

まずは、最も標準的なswitch文とdefaultの使い方を確認しましょう。

C#
using System;

class Program
{
    static void Main()
    {
        int dayOfWeek = 7; // 0:日曜 ... 6:土曜。ここでは範囲外の7を代入

        switch (dayOfWeek)
        {
            case 0:
                Console.WriteLine("今日は日曜日です。");
                break;
            case 6:
                Console.WriteLine("今日は土曜日です。");
                break;
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
                Console.WriteLine("平日は仕事や学校ですね。");
                break;
            default:
                // どのcaseにも該当しない場合の処理
                Console.WriteLine("エラー: 不正な曜日の値です。");
                break;
        }
    }
}
実行結果
エラー: 不正な曜日の値です。

C#のswitch文では、各caseの末尾にbreakreturnthrowなどを記述して、処理を抜ける必要があります。

これは「フォールスルー(あるcaseから次のcaseへ勝手に移動すること)」を禁止しているC#の言語仕様によるものです。

defaultラベルも例外ではなく、処理の最後には必ず制御を抜ける記述が必要となります。

defaultを記述する位置

一般的にdefaultswitch文の最後に記述されますが、言語仕様上はどこに記述しても問題ありません

例えば、最初にdefaultを書いても動作は同じです。

しかし、ソースコードを読み取る開発者の直感に合わせるため、特別な理由がない限りは最後に配置するのがベストプラクティスです。

なぜdefaultが必要なのか:その重要性と役割

プログラムにおいて、変数が常に想定通りの値を持っているとは限りません。

外部からの入力やデータベースの値、APIのレスポンスなどは、予期せぬ変更やバグによって未知の値が混入する可能性があります。

1. セーフティネットとしての役割

defaultを省略した場合、どのcaseにも一致しないときは「単に何も実行されない」という状態になります。

これは一見問題ないように見えますが、「値が不正であったことに気づかないまま後続の処理が進んでしまう」というリスクを孕んでいます。

defaultでログを出力したり例外をスローしたりすることで、不具合の早期発見に繋がります。

2. 網羅性の担保

特に後述するenum(列挙型)を使用する場合、新しい値が追加された際に対応が漏れてしまうことがあります。

defaultでガードを固めておくことで、ロジックの抜け穴を塞ぐことができます。

switch式における「_」(アンダースコア)との違い

C# 8.0以降、より簡潔に値を返すことができるswitch式(switch expression)が導入されました。

ここで多くの開発者が戸惑うのが、defaultキーワードではなく「_(アンダースコア)」という記号が使われる点です。

switch式での記述例

従来のswitch文と、新しいswitch式を比較してみましょう。

C#
// 従来のswitch文
string message;
switch (code)
{
    case 200: message = "OK"; break;
    case 404: message = "Not Found"; break;
    default:  message = "Unknown"; break;
}

// C# 8.0以降のswitch式
string message = code switch
{
    200 => "OK",
    404 => "Not Found",
    _   => "Unknown" // これがdefaultに相当する
};

「_」は「棄却パターン」

_は、C#において「棄却(Discard)」と呼ばれる特殊なパターンです。

「どんな値にも一致するが、その値自体は利用しない」という意味を持ちます。

なぜdefaultではなく_が使われるのでしょうか。

それは、switch式が「パターンマッチング」に基づいているからです。

パターンマッチングの世界では、特定の定数に一致させるだけでなく、「型が何か」「特定のプロパティが何か」といった条件で分岐させます。

その中で「その他すべて」を表現する最も汎用的なパターンが、この棄却パターン(_)なのです。

switch文とswitch式の使い分け表

項目switch文switch式
用途複雑な処理や制御フローの分岐値の評価と結果の代入(簡潔な変換)
デフォルトの指定default:_ =>
必須条件省略可能(何もしない)網羅性が必要(省略すると警告・エラーの可能性)
戻り値なし(void的)あり(式として値を返す)

実践的な活用シーン:enumとdefault

実務でswitch文が最も多用される場面の一つが、enum(列挙型)の判定です。

ここでは、defaultをどのように活用すべきか具体的なテクニックを紹介します。

列挙型の追加によるバグを防ぐ

例えば、会員ランクを判定するプログラムを考えてみましょう。

C#
public enum MembershipRank
{
    Guest,
    Regular,
    Gold
}

public string GetDiscountRate(MembershipRank rank)
{
    return rank switch
    {
        MembershipRank.Guest => "0%",
        MembershipRank.Regular => "5%",
        MembershipRank.Gold => "15%",
        _ => throw new ArgumentOutOfRangeException(nameof(rank), $"未定義のランク: {rank}")
    };
}

ここで、将来的に MembershipRank.Platinum が追加されたとします。

もし_(またはdefault)で例外を投げるようにしておけば、修正漏れがあった際にプログラムは即座にエラーを出し、「未対応のランクが存在する」という事実を開発者に知らせてくれます

もしdefaultで適当な値を返したり、何も処理を記述しなかったりすると、プラチナ会員なのに割引率が「不明」として処理される、といった深刻なビジネスロジック上のミスを見逃すことになります。

defaultを使わない「網羅的」なswitch

モダンなC#開発では、あえてdefaultを書かないことが推奨されるケースもあります。

これは「網羅性のチェック(Exhaustiveness check)」をコンパイラに任せる手法です。

コンパイラの警告を活用する

C#のコンパイラは、switch式において全てのenum値がカバーされているかをチェックできます。

C#
// もし全てのenum値をcaseに記述しているなら...
return rank switch
{
    MembershipRank.Guest => "0%",
    MembershipRank.Regular => "5%",
    MembershipRank.Gold => "15%",
    // ここで _ => を書かない
};

あえてdefault_)を書かずに全ケースを網羅しておくと、新しいenum値が追加されたときにコンパイラが「警告」を出してくれます。

defaultを書いてしまうと、「その他すべて」に含まれてしまうため、この便利な警告が出なくなります。

「何でも受け入れるdefault」か「厳密な全ケース網羅」かは、システムの設計思想に合わせて選択する必要があります。

高度なパターンマッチングとdefaultの組み合わせ

C# 9.0や10.0以降、パターンマッチングはさらに進化しました。

default相当の処理を行う際にも、より詳細な条件指定が可能です。

リレーショナルパターンとの併用

C#
int score = 85;
string grade = score switch
{
    >= 90 => "S",
    >= 80 => "A",
    >= 70 => "B",
    _ when score < 0 => "Invalid", // 条件付きの棄却
    _ => "C" // 最終的なdefault
};

このように、_when句を組み合わせることで、「特定の条件を満たさないその他すべて」をさらに細かく制御できます。

defaultに関するよくある疑問と注意点

switch文の中でdefaultは必須ですか?

文法上は必須ではありません。

しかし、どの条件にも当てはまらない場合の挙動が不明確なときは必ず記述すべきです。

特に値を返す switch 式では網羅されていないと実行時に SwitchExpressionException が発生します。

default: break; だけで意味はありますか?

あります。

明示的に何もしないことを示すことで、後からコードを読む人に「このケースは意図的に無視している」ことを伝えます。

例: default: break;

defaultとnullチェックの関係は?

switch(obj) のように参照型を扱う場合、 defaultnull にも一致します。

ただし、明示的に case null: を記述している場合はそちらが優先されます。

まとめ

C#のswitch文におけるdefault、およびswitch式における「_」は、単なる「その他」の処理を記述する場所ではありません。

それは、予期せぬエラーからアプリケーションを守り、ロジックの正確性を担保するための重要な砦です。

本記事のポイントをまとめます。

  • switch文のdefault: 従来の構文で「その他すべて」を処理する。最後に配置するのが一般的。
  • switch式の「_」: パターンマッチングにおける「棄却」を利用したモダンな記述。
  • セーフティネット: 予期せぬ値に対しては例外を投げるか、ログを出す実装が推奨される。
  • 網羅性の検討: あえてdefaultを書かずコンパイラの警告を利用する手法も有効。

C#の進化に伴い、条件分岐の書き方はより宣言的で安全なものへと変わってきました。

最新の構文とdefaultの役割を正しく理解し、バグの少ないクリーンなコードを目指しましょう。