switch 式 (C# リファレンス)switch expression (C# reference)

この記事では、C# 8.0 で導入された switch 式について説明します。This article covers the switch expression, introduced in C# 8.0. switch ステートメントについては、ステートメントのセクションの switch ステートメントに関する記事を参照してください。For information on the switch statement, see the article on the switch statement in the statements section.

基本的な例Basic example

switch 式では、式のコンテキストにおいて switch と同様のセマンティクスが提供されます。The switch expression provides for switch-like semantics in an expression context. switch アームで値が生成されるときの簡潔な構文が提供されます。It provides a concise syntax when the switch arms produce a value. 次の例では、switch 式の構造を示します。The following example shows the structure of a switch expression. オンライン マップでの視方向を表す enum の値が、対応する基本方位に変換されます。It translates values from an enum representing visual directions in an online map to the corresponding cardinal direction:

public static class SwitchExample
{
    public enum Directions
    {
        Up,
        Down,
        Right,
        Left
    }

    public enum Orientation
    {
        North,
        South,
        East,
        West
    }

    public static void Main()
    {
        var direction = Directions.Right;
        Console.WriteLine($"Map view direction is {direction}");

        var orientation = direction switch
        {
            Directions.Up    => Orientation.North,
            Directions.Right => Orientation.East,
            Directions.Down  => Orientation.South,
            Directions.Left  => Orientation.West,
        };
        Console.WriteLine($"Cardinal orientation is {orientation}");
    }
}

前のサンプルでは、switch 式の基本的な要素が示されています。The preceding sample shows the basic elements of a switch expression:

  • "範囲式": 前の例では、変数 direction が範囲式として使用されています。The range expression: The preceding example uses the variable direction as the range expression.
  • "switch 式アーム": 各 switch 式アームには、"パターン"、省略可能な "ケース ガード"、=> トークン、"" が含まれています。The switch expression arms: Each switch expression arm contains a pattern, an optional case guard, the => token, and an expression.

"switch 式の結果" は、"パターン" が "範囲式" と一致し、"ケース ガード" が true と評価される (存在する場合)、最初の "switch 式アーム" の式の値です。The result of the switch expression is the value of the expression of the first switch expression arm whose pattern matches the range expression and whose case guard, if present, evaluates to true. => トークンの右側の "" として、式ステートメントを使用することはできません。The expression on the right of the => token can't be an expression statement.

"switch 式アーム" は、テキストの順番に評価されます。The switch expression arms are evaluated in text order. 高い "switch 式アーム" がすべての値と一致するため、低い "switch 式アーム" を選択できない場合、コンパイラではエラーが発行されます。The compiler issues an error when a lower switch expression arm can't be chosen because a higher switch expression arm matches all its values.

パターンとケース ガードPatterns and case guards

switch 式アームでは、多くのパターンがサポートされています。Many patterns are supported in switch expression arms. 前の例では "定数パターン" が使用されています。The preceding example uses a constant pattern. "定数パターン" では、範囲式と値が比較されます。A constant pattern compares the range expression to a value. その値は、コンパイル時定数である必要があります。That value must be a compile-time constant. "型パターン" では、範囲式と既知の型が比較されます。The type pattern compares the range expression to a known type. 次の例では、シーケンスから 3 番目の要素が取得されます。The following example retrieves the third element from a sequence. シーケンスの型に基づいて、異なるメソッドが使用されます。It uses different methods based on the type of the sequence:

public static T TypeExample<T>(IEnumerable<T> sequence) =>
    sequence switch
    {
        System.Array array => (T)array.GetValue(2),
        IList<T> list      => list[2],
        IEnumerable<T> seq => seq.Skip(2).First(),
    };

パターンは再帰的にすることができます。パターンで型をテストし、その型が一致する場合、そのパターンは範囲式の 1 つまたは複数のプロパティ値と一致します。Patterns can be recursive, where a pattern tests a type, and if that type matches, the pattern matches one or more property values on the range expression. 再帰パターンを使用して、前の例を拡張できます。You can use recursive patterns to extend the preceding example. 要素数が 3 未満の配列に対する switch 式アームを追加します。You add switch expression arms for arrays that have fewer than 3 elements. 再帰パターンを次の例に示します。Recursive patterns are shown in the following example:

public static T RecursiveExample<T>(IEnumerable<T> sequence) =>
    sequence switch
    {
        System.Array { Length : 0}       => default(T),
        System.Array { Length : 1} array => (T)array.GetValue(0),
        System.Array { Length : 2} array => (T)array.GetValue(1),
        System.Array array               => (T)array.GetValue(2),
        IList<T> list                    => list[2],
        IEnumerable<T> seq               => seq.Skip(2).First(),
    };

再帰パターンでは、範囲式のプロパティを調べることはできますが、任意のコードを実行することはできません。Recursive patterns can examine properties of the range expression, but can't execute arbitrary code. when 句で指定する "ケース ガード" を使用して、他のシーケンス型と同様のチェックを行うことができます。You can use a case guard, specified in a when clause, to provide similar checks for other sequence types:

public static T CaseGuardExample<T>(IEnumerable<T> sequence) =>
    sequence switch
    {
        System.Array { Length : 0}                => default(T),
        System.Array { Length : 1} array          => (T)array.GetValue(0),
        System.Array { Length : 2} array          => (T)array.GetValue(1),
        System.Array array                        => (T)array.GetValue(2),
        IEnumerable<T> list when !list.Any()      => default(T),
        IEnumerable<T> list when list.Count() < 3 => list.Last(),
        IList<T> list                             => list[2],
        IEnumerable<T> seq                        => seq.Skip(2).First(),
    };

最後に、_ パターンと null パターンを追加して、他のどの switch 式アームによっても処理されない引数をキャッチすることができます。Finally, you can add the _ pattern and the null pattern to catch arguments that aren't processed by any other switch expression arm. そのようにすると、switch 式が "網羅的" になります。これは、範囲式で可能性のあるすべての値が処理されることを意味します。That makes the switch expression exhaustive, meaning any possible value of the range expression is handled. 次の例では、そのような式アームを追加しています。The following example adds those expression arms:

public static T ExhaustiveExample<T>(IEnumerable<T> sequence) =>
    sequence switch
    {
        System.Array { Length : 0}       => default(T),
        System.Array { Length : 1} array => (T)array.GetValue(0),
        System.Array { Length : 2} array => (T)array.GetValue(1),
        System.Array array               => (T)array.GetValue(2),
        IEnumerable<T> list
            when !list.Any()             => default(T),
        IEnumerable<T> list
            when list.Count() < 3        => list.Last(),
        IList<T> list                    => list[2],
        null                             => throw new ArgumentNullException(nameof(sequence)),
        _                                => sequence.Skip(2).First(),
    };

前の例では、null パターンが追加され、IEnumerable<T> 型パターンが _ パターンに変更されています。The preceding example adds a null pattern, and changes the IEnumerable<T> type pattern to a _ pattern. null パターンでは、switch 式アームとして null チェックが提供されます。The null pattern provides a null check as a switch expression arm. そのアームの式によって、ArgumentNullException がスローされます。The expression for that arm throws an ArgumentNullException. _ パターンは、それより前にあるアームで一致していないすべての入力と一致します。The _ pattern matches all inputs that haven't been matched by previous arms. null チェックの後で指定する必要があります。そうしないと、null 入力と一致します。It must come after the null check, or it would match null inputs.

網羅的でない switch 式Non-exhaustive switch expressions

switch 式のどのパターンでも引数がキャッチされない場合、ランタイムで例外がスローされます。If none of a switch expression's patterns catches an argument, the runtime throws an exception. .NET Core 3.0 以降のバージョンでは、例外は System.Runtime.CompilerServices.SwitchExpressionException です。In .NET Core 3.0 and later versions, the exception is a System.Runtime.CompilerServices.SwitchExpressionException. .NET Framework では、例外は InvalidOperationException です。In .NET Framework, the exception is an InvalidOperationException.

関連項目See also