次の方法で共有


11 パターンとパターン マッチング

11.1 全般

パターンは構文形式であり、is演算子 (§12.14.12)、switch_statement (§13.8.3)、およびswitch_expression (§12.11) で使用して、受信データの比較対象となるデータの形状を表すことができます。 データの一部を サブパターンと照合できるように、パターンは再帰的な場合があります。

パターンは、さまざまなコンテキストの値に対してテストされます。

  • switch ステートメントでは、ケース ラベルの パターン が switch ステートメントの に対してテストされます。
  • is-pattern 演算子では、右側のパターンが左側の式に対してテストされます。
  • switch 式では、switch_expression_armパターンが switch 式の左側の式に対してテストされます。
  • 入れ子になったコンテキストでは、 サブパターンは、 パターンの形式に応じて、プロパティ、フィールド、または他の入力値から取得された値に対してテストされます。

パターンがテストされる値を パターン入力値と呼ばれます。

11.2 パターン形式

11.2.1 全般

パターンには、次のいずれかの形式があります。

pattern
    : declaration_pattern
    | constant_pattern
    | var_pattern
    | positional_pattern
    | property_pattern
    | discard_pattern
    ;

一部の パターンs では、ローカル変数が宣言される可能性があります。

各パターン フォームは、パターンを適用できる入力値の型のセットを定義します。 パターン Pパターンが一致する可能性のある値を持つ型の中にTがある場合にT型に適用できます。 Pに適用できない場合、パターンTP型のパターン入力値 (T) と一致するようにプログラムに表示される場合、コンパイル時エラーになります。

: 次の例では、 v のコンパイル時の型が TextReaderされているため、コンパイル時エラーが生成されます。 TextReader型の変数は、stringと参照互換の値を持つことはありません。

TextReader v = Console.In; // compile-time type of 'v' is 'TextReader'
if (v is string) // compile-time error
{
    // code assuming v is a string
}

ただし、 v のコンパイル時の型が objectされているため、次のコードではコンパイル時エラーは生成されません。 object型の変数は、stringと参照互換の値を持つことができます。

object v = Console.In;
if (v is string s)
{
    // code assuming v is a string
}

end の例

各パターン フォームは、パターンが実行時に値 照合 値のセットを定義します。

パターン マッチング中の操作と副作用の評価順序 ( Deconstructの呼び出し、プロパティ アクセス、 System.ITuple内のメソッドの呼び出し) は指定されていません。

11.2.2 宣言パターン

declaration_patternは、値が特定の型を持っていることをテストし、テストが成功した場合は、必要に応じてその型の変数に値を提供するために使用されます。

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : discard_designation
    | single_variable_designation
    ;
discard_designation
    : '_'
    ;
single_variable_designation
    : identifier
    ;

トークン _を持つsimple_designationは、single_variable_designationではなくdiscard_designation見なされます

値のランタイム型は、is-type 演算子 (§12.14.12.1) で指定されたのと同じ規則を使用して、パターン内のに対してテストされます。 テストが成功すると、その値パターン照合されます。 型が null 許容値 (§8.3.12) または null 許容参照型 (§8.9.3) の場合、コンパイル時エラーです。 このパターン フォームは、 null 値と一致しません。

: is-type 式 e is T と宣言パターン e is T _ は、 T が null 許容型でない場合と同等です。 注釈

パターン入力値 (§11.1) e を指定すると、 simple_designationdiscard_designation場合は破棄 (§9.2.9.2) を表し、 e の値は何にもバインドされません。 ( _ という名前の宣言された変数は、その時点でスコープ内にある場合がありますが、その名前付き変数はこのコンテキストでは表示されません)。それ以外の場合、 simple_designationsingle_variable_designationされている場合は、指定された識別子によって指定された型のローカル変数 (§9.2.9) が導入されます。 そのローカル変数には、パターンが マッチしたときに、その値 パターン入力値の値が割り当てられます。

パターン入力値と特定の型の静的型の特定の組み合わせは互換性がないとみなされ、コンパイル時エラーが発生します。 静的型Eの値は、id 変換、暗黙的または明示的な参照変換、ボックス化変換、からへのボックス化解除の変換、またはTまたはEのいずれかが開いている型 (T) がある場合にE互換性があると言われます。 T型に名前を付ける宣言パターンはEEと互換性のあるすべての型Tに適用できます。

: オープン型のサポートは、構造体型またはクラス型である可能性がある型をチェックする場合に最も役立ち、ボックス化は避ける必要があります。 注釈

: 宣言パターンは、参照型の実行時の型テストを実行するのに役立ち、イディオムを置き換えます

var v = expr as Type;
if (v != null) { /* code using v */ }

少し簡潔に

if (expr is Type v) { /* code using v */ }

end の例

: 宣言パターンは、null 許容型の値をテストするために使用できます。型Nullable<T> (またはボックス化されたT) の値は、値が null 以外で T2 idT2T場合、またはTの基本型またはインターフェイスの型パターンと一致します。 たとえば、コード フラグメント内

int? x = 3;
if (x is int v) { /* code using v */ }

if ステートメントの条件は実行時にtrueされ、変数vはブロック内で3型の値intを保持します。 ブロックの後、変数 v はスコープ内にありますが、確実には割り当てません。 end の例

11.2.3 定数パターン

constant_patternは、パターン入力値 (§11.1) の値を特定の定数値に対してテストするために使用されます。

constant_pattern
    : constant_expression
    ;

定数パターンPTの定数式から型Pへの暗黙的な変換がある場合、型Tに適用できます。

定数パターン Pの場合、その 変換された値

  • パターン入力値の型が整数型または列挙型の場合、パターンの定数値はその型に変換されます。然も無くば
  • パターン入力値の型が整数型または列挙型の null 許容バージョンである場合、パターンの定数値は基になる型に変換されます。然も無くば
  • パターンの定数値の値。

パターン入力値がe変換された値を持つ定数パターンPv

  • eが整数型または列挙型、またはいずれかの null 許容形式を持ち、v が整数型を持つ場合、パターンPマッチの結果がe == v場合は値true。それ以外の場合
  • Pを返す場合は値が一致します。

: 次のメソッドの switch ステートメントでは、ケース ラベルに 5 つの定数パターンが使用されます。

static decimal GetGroupTicketPrice(int visitorCount)
{
    switch (visitorCount) 
    {
        case 1: return 12.0m;
        case 2: return 20.0m;
        case 3: return 27.0m;
        case 4: return 32.0m;
        case 0: return 0.0m;
        default: throw new ArgumentException(...);
    }
}

end の例

11.2.4 Var パターン

var_patternmatchesすべての値。 つまり、 var_pattern を使用したパターン マッチング操作は常に成功します。

var_patternすべての型に適用できます。

var_pattern
    : 'var' designation
    ;
designation
    : simple_designation
    | tuple_designation
    ;
tuple_designation
    : '(' designations? ')'
    ;
designations
    : designation (',' designation)*
    ;

パターン入力値 (§11.1) e を指定すると、指定がdiscard_designation場合は破棄 (§9.2.9.2) を表し、e の値は何にもバインドされません。 (その名前を持つ宣言された変数は、その時点でスコープ内にある場合がありますが、このコンテキストではその名前付き変数は表示されません)。それ以外の場合、指定がsingle_variable_designation場合、実行時に e の値は、その名前の新しく導入されたローカル変数 (§9.2.9) にバインドされ、その型は e の静的型であり、パターン入力値はそのローカル変数に割り当てられます。

varが使用されている型に名前がバインドされると、エラーになります。

指定tuple_designationの場合、パターンはフォーム (varデザインpositional_pattern (§11.2.5) に相当します。 )指定は、tuple_designation内で見つかったものです。 たとえば、パターン var (x, (y, z))(var x, (var y, var z))と同じです。

11.2.5 位置パターン

positional_patternは、入力値がnullされていないことを確認し、適切なDeconstruct メソッド (§12.7) を呼び出し、結果の値に対してさらにパターン マッチングを実行します。 また、入力値の型が Deconstructを含む型と同じ場合、または入力値の型がタプル型の場合、または入力値の型が object または System.ITuple であり、式のランタイム型が System.ITuple実装されている場合は、タプルに似たパターン構文 (型が指定されていない) もサポートされます。

positional_pattern
    : type? '(' subpatterns? ')' property_subpattern? simple_designation?
    ;
subpatterns
    : subpattern (',' subpattern)*
    ;
subpattern
    : pattern
    | identifier ':' pattern
    ;

入力値がパターン (subpatterns)に一致する場合、メソッドを選択するには、 Deconstruct のアクセス可能な宣言を検索し、分解宣言と同じ規則を使用してそのうちの 1 つを選択します。 positional_patternが型を省略し、識別子のない単一のサブパターンを持ち、property_subpatternがなく、simple_designationがない場合はエラーです。 これにより、親子化された constant_patternpositional_patternの間であいまいさが解消されます。 リスト内のパターンと一致する値を抽出するには、

  • が省略され、入力式の型がタプル型の場合、サブパターンの数はタプルのカーディナリティと同じになります。 各タプル要素は対応する サブパターンと照合され、これらすべてが成功した場合は一致が成功します。 サブパターン識別子がある場合は、タプル型の対応する位置にタプル要素の名前を付けます。
  • それ以外の場合、適切なDeconstructが型のメンバーとして存在する場合、入力値の型が型とパターン互換でない場合はコンパイル時エラーになります。 実行時に、入力値は に対してテストされます。 これが失敗した場合、位置指定パターンの一致は失敗します。 成功した場合、入力値はこの型に変換され、 Deconstruct はコンパイラによって生成された新しい変数を使用して呼び出され、出力パラメーターを受け取ります。 受信した各値は対応する サブパターンと照合され、これらすべてが成功した場合は一致が成功します。 サブパターン識別子がある場合は、Deconstructの対応する位置にパラメーターの名前を付けます。
  • それ以外の場合、 が省略され、入力値が object 型または暗黙的な参照変換によって System.ITuple に変換できる型であり、サブパターン間に 識別子 が表示されない場合、一致では System.ITupleが使用されます。
  • それ以外の場合、パターンはコンパイル時エラーです。

実行時にサブパターンが一致する順序は指定されておらず、一致が失敗してもすべてのサブパターンとの一致が試行されない場合があります。

: ここでは、式の結果を分解し、結果の値を対応する入れ子になったパターンと照合します。

static string Classify(Point point) => point switch
{
    (0, 0) => "Origin",
    (1, 0) => "positive X basis end",
    (0, 1) => "positive Y basis end",
    _ => "Just a point",
};

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) => (X, Y) = (x, y);
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

end の例

: タプル要素と分解パラメーターの名前は、次のように位置指定パターンで使用できます。

var numbers = new List<int> { 10, 20, 30 };
if (SumAndCount(numbers) is (Sum: var sum, Count: var count))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");
}

static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return (sum, count);
}

生成される出力は次のとおりです。

Sum of [10 20 30] is 60

end の例

11.2.6 プロパティ パターン

property_patternは、入力値がnullされていないことを確認し、アクセス可能なプロパティまたはフィールドを使用して抽出された値と再帰的に一致します。

property_pattern
    : type? property_subpattern simple_designation?
    ;
property_subpattern
    : '{' '}'
    | '{' subpatterns ','? '}'
    ;

property_patternサブパターン識別子が含まれていない場合はエラーです。

型が null 許容値 (§8.3.12) または null 許容参照型 (§8.9.3) の場合、コンパイル時エラーです。

: null チェック パターンは、単純なプロパティ パターンから除外されます。 文字列 s が null 以外かどうかを確認するには、次のいずれかの形式を記述できます。

#nullable enable
string s = "abc";
if (s is object o) ...  // o is of type object
if (s is string x1) ... // x1 is of type string
if (s is {} x2) ...     // x2 is of type string
if (s is {}) ...

end notee がパターン{property_pattern_list}に一致すると、式 e型で指定された型 T とパターン互換でない場合、コンパイル時エラーになります。 型が存在しない場合、型は e の静的型であると見なされます。 property_pattern_listの左側に表示される各識別子は、アクセス可能なプロパティまたは T フィールドを指定する必要があります。property_patternsimple_designationが存在する場合は、T 型のパターン変数を宣言します。

実行時に、式は T に対してテストされます。これが失敗した場合、プロパティ パターンの一致は失敗し、結果は false。 成功した場合、各 property_subpattern フィールドまたはプロパティが読み取られ、その値が対応するパターンと一致します。 一致全体の結果は、これらのいずれかの結果がfalseされた場合にのみfalseされます。 サブパターンが一致する順序は指定されておらず、失敗した一致では実行時にすべてのサブパターンがテストされない場合があります。 一致が成功し、property_patternのsimple_designationsingle_variable_designationの場合、宣言された変数には一致した値が割り当てられます。

property_patternは、匿名型とのパターンマッチングに使用できます。

例:

var o = ...;
if (o is string { Length: 5 } s) ...

end の例

: 次のように、実行時の型チェックと変数宣言をプロパティ パターンに追加できます。

Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello
Console.WriteLine(TakeFive("Hi!"));            // output: Hi!
Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345
Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc

static string TakeFive(object input) => input switch
{
    string { Length: >= 5 } s => s.Substring(0, 5),
    string s => s,
    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),
    ICollection<char> symbols => new string(symbols.ToArray()),
    null => throw new ArgumentNullException(nameof(input)),
    _ => throw new ArgumentException("Not supported input type."),
};

生成される出力は次のとおりです。

Hello
Hi!
12345
abc

end の例

11.2.7 破棄パターン

すべての式が破棄パターンと一致し、その結果、式の値が破棄されます。

discard_pattern
    : '_'
    ;

フォームのrelational_expressionisパターンrelational_expressionまたはswitch_labelのパターンとして破棄パターンを使用するのは、コンパイル時エラーです。

: そのような場合は、任意の式と一致させるために、破棄var _でvar_patternを使用します。 注釈

例:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));
Console.WriteLine(GetDiscountInPercent(null));
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));

static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch
{
    DayOfWeek.Monday => 0.5m,
    DayOfWeek.Tuesday => 12.5m,
    DayOfWeek.Wednesday => 7.5m,
    DayOfWeek.Thursday => 12.5m,
    DayOfWeek.Friday => 5.0m,
    DayOfWeek.Saturday => 2.5m,
    DayOfWeek.Sunday => 2.0m,
    _ => 0.0m,
};

生成される出力は次のとおりです。

5.0
0.0
0.0

ここでは、破棄パターンを使用して、 null と、 DayOfWeek 列挙体の対応するメンバーを持たない整数値を処理します。 これにより、 switch 式が使用可能なすべての入力値を処理することが保証されます。 end の例

11.3 パターンのサブ再開

switch ステートメントでは、前の一連の非ガード ケース (§13.8.3) によって、ケースのパターンがsubsumed場合はエラーになります。 非公式には、これは、入力値が前のいずれかのケースで一致したことを意味します。 次の規則は、一連のパターンが特定のパターンをサブスムするタイミングを定義します。

パターンが一致Pそのパターンのランタイム動作の仕様がKに一致P場合は定数K

一連のパターン Qsubsumes 次のいずれかの条件が満たされている場合に P パターンです。

  • P は定数パターンであり、セット Q 内のすべてのパターンが P変換された値と一致します
  • Pは var パターンであり、パターン入力値の型 (Q) に対してパターンのセットが exhaustive (§11.4) であり、パターン入力値が null 許容型でないか、Qのパターンがnullに一致します。
  • Pは型Tを持つ宣言パターンであり、型Q (§11.4) に対してTパターンのセットがです。

11.4 パターンの網羅性

非公式には、null 以外の型のすべての可能な値に対して、セット内のパターンが適用される場合、パターンのセットは完全です。 次の規則は、一連のパターンが型に対していつ 完全 するかを定義します。

次のいずれかの条件が満たされている場合、型Q完全です。

  1. T は整数型または列挙型、またはいずれかの null 許容バージョンであり、 Tの null 非許容基になる型のすべての可能な値に対して、 Q のパターンはその値と一致します。
  2. Qの一部のパターンは、var パターン; または
  3. Qのパターンの中には、型D宣言パターンがあり、ID 変換、暗黙的な参照変換、またはTからDへのボックス化変換があります。

例:

static void M(byte b)
{
    switch (b) {
        case 0: case 1: case 2: ... // handle every specific value of byte
            break;
        // error: the pattern 'byte other' is subsumed by the (exhaustive)
        // previous cases
        case byte other: 
            break;
    }
}

end の例