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

11.1 全般

パターンは、is演算子 (§12.15.12)、switch_statement (§13.8.3)、およびswitch_expression (§12.12) で使用して、受信データの比較対象となるデータの形状を記述できます。 データの一部を サブパターンと照合して、パターンを入れ子にすることができます。

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

  • switch_statementでは、switch_labelのパターンがswitch_statementのselector_expressionに対してテストされます。
  • is-pattern 演算子を使用すると、右側のパターンが左側の式に対してテストされます。
  • switch_expressionでは、switch_expressionの左側にある式に対してswitch_expression_armのパターンがテストされます。
  • 入れ子になったコンテキストでは、 サブパターンは、 パターンの形式に応じて、プロパティ、フィールド、または他の入力値から取得された値に対してテストされます。

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

パターン Pは、Pで一致する入力値がQのメンバーの 1 つによって一致する場合にQ一連の非ガード パターンによってサブスム化されます。

switch ステートメント (§13.8.3) では、ケースのパターンが前の一連の非ガード (§13.8.3) ケースによってサブスムされるとエラーになります。 switch 式 (§12.12) では、switch_expression_armのパターンが前の一連の無防備なswitch_expression_armパターンによってサブスムされると、エラーになります。

可能なすべての入力値に対してセット内のパターンが適用される場合、パターンのセットは完全です。 一連のパターンが完全ではないことが実装によって検出されると、警告が発行されます。

11.2 パターン形式

11.2.1 全般

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

pattern
    : logical_pattern
    ;

primary_pattern
    : parenthesized_pattern
    | declaration_pattern
    | constant_pattern
    | var_pattern
    | positional_pattern
    | property_pattern
    | discard_pattern
    | type_pattern
    | relational_pattern
    | list_pattern
    | slice_pattern
    ;

parenthesized_pattern
    : '(' pattern ')'
    ;

'(' pattern ')'実稼働では、パターンをかっこで囲んで、logical_patternのいずれかを使用して組み合わせたパターン間の評価順序を適用できます。

入力をconstant_patternとpositional_patternの両方として構文的に認識できる場合は、constant_patternを選択する必要があります。

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

各パターン フォームは、パターンを適用できる入力値の型のセットを定義します。 パターン Pはパターンが一致する可能性のある値を持つ型の中にTがある場合にT型に適用できます。 Pがに適用できない場合、パターンTがP型のパターン入力値 (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.Runtime.CompilerServices.ITupleのメンバーの呼び出し) は指定されていません。

11.2.2 宣言パターン

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

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

discard_designationとsingle_variable_designationの両方の代替手段が適用される場合、simple_designationを認識する場合は、前者を選択する必要があります。

注: ANTLR は、simple_designationの代替の順序により、指定された選択を自動的に行います。 注釈

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

値のランタイム型は、is-type 演算子 (§12.15.12.1) で指定されたのと同じ規則を使用して、パターン内の型に対してテストされます。 テストが成功すると、その値パターン照合されます。

注: is-type 式 e is T と宣言パターン e is T _ は、両方が有効な場合は同等です。 注釈

パターン入力値 (§11.1) e を指定すると、 simple_designation が discard_designationで、破棄 (§9.2.9.2) を示す場合、 e の値は何にもバインドされません。 それ以外の場合、 simple_designation が single_variable_designationの場合は、指定された識別子によって指定された型のローカル変数 (§9.2.9) が導入されます。 そのローカル変数には、パターンが マッチしたときに、その値 パターン入力値の値が割り当てられます。

注: _内ののこの処理は、_として記述されたスタンドアロン (§11.2.7) とは異なります。後者の場合、スコープ内定数または _ という名前の型は非表示になりません。 注釈

型Eは、id 変換、暗黙的または明示的な参照変換、ボックス化変換、ボックス化解除変換、またはからへの暗黙的または明示的な null 許容値の型変換、またはTまたはEのいずれかがオープン型 (T) である場合にE型とT言われます。 T型に名前を付ける宣言パターンは、がと互換性のあるパターンであるすべての型E (E) にT。 静的な型Tがと互換性のないパターン入力値 (E) と一致するために、型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 idT2がT場合、または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
    ;

定数パターンPは、Pの定数式から型Tへの暗黙的な変換がある場合、またはTがSystem.Span<char>またはSystem.ReadOnlySpan<char>であり、Pの定数式が型stringであり、nullリテラルでない場合に、型Tに適用されます。

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

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

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

  • eが整数型または列挙型、またはいずれかの null 許容形式を持ち、v が整数型を持つ場合、パターンPマッチ式の結果がe == v場合は値true。それ以外の場合
  • e がSystem.Span<char>型またはSystem.ReadOnlySpan<char>型で、v が定数文字列で、v に定数値がnullでない場合、System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(v))がtrueを返す場合、パターンは e の値と一致P。それ以外の場合
  • 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 (§11.2.5) に相当します。 )指定は、tuple_designation内で見つかったものです。 たとえば、パターン var (x, (y, z)) は (var x, (var y, var z))と同じです。

11.2.5 位置パターン

positional_patternは、入力値がnullされていないことを確認し、そこから値のシーケンスを抽出し、抽出された各値を対応するサブパターンと照合します。 値は、3 つの方法のいずれかで抽出されます。入力をタプルとして扱うか、 Deconstruct メソッドを呼び出すか、 System.Runtime.CompilerServices.ITupleを使用して入力にインデックスを付けます。

注: ここでの Deconstruct の使用は、 §12.7 で定義されているソース レベルの分解変換とは異なります。 注釈

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

かっこの間に出現するサブパターンの数を n にします。 一致戦略は、コンパイル時に次のケースを順番に適用することによって選択されます。条件が満たされている最初のケースが使用され、残りのケースは考慮されません。 ケースが選択されると、その戦略がコミットされます。そのケース内に記載されているコンパイル時エラーが報告され、一致は後続のケースには反映されません。

  1. タプル フォーム。 型を省略し、入力値の静的型がタプル型 (§8.3.11) の場合、または入力値がタプル リテラル (§12.8.6) の場合は、このケースが適用されます。 n がそのタプル型のアリティと等しくない場合、コンパイル時エラーになります。 実行時に、各タプル要素は対応する サブパターンと照合されます。これらのすべてが成功した場合、一致は成功します。 サブパターンに識別子がある場合、その識別子はタプル型の対応する位置にタプル要素の名前を付けます。
  2. フォームを分解します。 それ以外の場合、いずれかの 型 が存在するか、 型 が省略され、入力値の静的型にアクセス可能な Deconstruct メソッド (§12.7) が含まれている場合は、このケースが適用されます。 型が存在する場合は D を型にする。それ以外の場合は、D を入力値の静的な型にすることができます。 Deconstruct メソッドは、分解宣言と同じオーバーロード解決規則を使用して D から選択されます。out パラメーターの数が n に等しいという追加の要件があります。そのようなメソッドが存在しない場合はコンパイル時エラーです。 型が存在する場合、入力値の静的型が型とパターン互換性がない (§11.2.2) 場合、コンパイル時エラーになります。実行時に入力値が型に対してテストされ、そのテストが失敗した場合、位置パターンの一致は失敗します。 それ以外の場合、入力値は D に変換され、選択した Deconstruct メソッドは、 out パラメーターを受け取る新しい変数で呼び出されます。 受信した各値は対応する サブパターンと照合され、これらすべてが成功した場合は一致が成功します。 サブパターンに識別子がある場合、その識別子はパラメーターにDeconstructの対応する位置に名前を付けます。
  3. ITuple フォーム。 それ以外の場合、 型 を省略すると、 サブパターン に 識別子がなく、入力値の静的な型が object、 System.Runtime.CompilerServices.ITuple、または System.Runtime.CompilerServices.ITupleへの暗黙的な参照変換を持つ型である場合、このケースが適用されます。 実行時に、入力値がnullのSystem.Runtime.CompilerServices.ITuple以外のインスタンスであることがテストされます。そのテストが失敗した場合、位置指定パターンの一致は失敗します。 それ以外の場合、値の Length プロパティが読み取られ、 n と等しくない場合、位置指定パターンの一致は失敗します。 それ以外の場合は、 i が 1 から n の各 i について、 i − 1 で入力値のインデックスを作成した値が i 番目の サブパターンと照合され、これらすべてが成功した場合に一致が成功します。
  4. それ以外の場合、大文字と小文字は適用されません。 positional_pattern はコンパイル時エラーです。

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

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

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 (SumAndAverage(numbers) is (Sum: var sum, Average: var average))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}; average is {average}");
}
else
{
    // Note: sum and average are in scope here, but not definitely assigned
    Console.WriteLine("No numbers provided to compute sum and average.");   
}

static (double Sum, double Average)? SumAndAverage(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return count == 0 ? null : (sum, sum / count);
}

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

Sum of [10 20 30] is 60; average is 20

end の例

11.2.6 プロパティ パターン

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

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

property_patternのサブパターンにsubpattern_nameが含まれていない場合はエラーです。

型が 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 {}) ...

x2を宣言する例は、変数型を推論するという点でif (s is var x2)に似ていますが、プロパティ パターンでは、x2が null 以外であることが保証されます。 注釈

式 e がパターン型{subpatterns} に一致すると、式 e が型によって指定された型 T とパターン互換性がない (§11.2.2) 場合、コンパイル時エラーになります。 型が存在しない場合、型は e の静的型であると見なされます。 サブパターンの左側に表示される各subpattern_nameは、アクセス可能な読み取り可能なプロパティまたは T フィールドを指定する必要があります。property_patternのsimple_designationが存在する場合は、T 型のパターン変数を宣言します。

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

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

subpattern_nameは、入れ子になったメンバーを参照できます。 このような場合、各名前検索の受信側は、property_patternの入力の種類から始まる、前のメンバー T₀ の型です。 T が null 許容型の場合、T₀ はその基になる型であり、それ以外の場合 T₀ は T と等しくなります。たとえば、フォーム { Prop1.Prop2: pattern }のパターンは、{ Prop1: { Prop2: pattern } }とまったく同じです。

注: これには、T が null 許容値型または参照型である場合の null チェックが含まれます。 この null チェックは、使用できる入れ子になったプロパティが T ではなく T₀のプロパティであることを意味します。繰り返しメンバー パスが許可されるため、パターン マッチングのコンパイルでは、パターンの一般的な部分を利用できます。 注釈

例:

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
    : '_'
    ;

構文コンテキストでパターンが許可されている場合、トークン_がアクセス可能な定数または型に対するsimple_name (§12.8.4) として解決される場合、_はdiscard_patternとして扱われません。 その代わりに:

  • _アクセス可能な定数に解決された場合、_は定数式がその定数であるconstant_pattern (§11.2.3) として解釈されます。
  • _が型に解決された場合、is演算子の右側では、コンストラクト relational_expressionis _はその型に対する is-type 演算子 (§12.15.12.1) テストとして解釈されます。 パターンを認めるその他の構文コンテキストでは、型に解決するベア _自体は有効なパターンではありません。ただし、_は、declaration_patternの型 (_ x など) として、または型に明示的に名前を付ける他のパターン 形式として表示される場合があります。

この規則は、破棄パターンを導入する前に、 _ を型または識別子として定義したコードとの下位互換性を維持します。 _アクセス可能な定数または型 (ローカル変数、パラメーター、フィールド、メソッドなど) 以外に解決された場合、ルールは適用されず、_はdiscard_patternのままです。

注: これはvar のの規則に似ていますが、スコープ内定数または型_の場合、_がエラーを生成するのではなく、その宣言への参照として解釈される点が異なります。 注釈

上記の規則を適用した後も、トークン _がまだdiscard_patternである場合、そのdiscard_patternがフォームrelational_expressionパターンのis全体のパターンとして、またはswitch_labelのパターン全体として表示されるコンパイル時エラーになります。 ただし、discard_patternは外側のパターンのサブパターンとして表示される場合があります (たとえば、positional_patternまたはproperty_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 の例

例: _という名前のスコープ内定数によって、_式内のswitch arm の解釈がどのように変更されるかを示します。 WithoutUnderscoreでは、_アームはdiscard_patternであり、任意の値と一致します。 WithUnderscoreでは、スコープ内定数_により、_ arm は0にのみ一致するconstant_patternとして解釈されます。

static string WithoutUnderscore(int n) => n switch
{
    1 => "one",
    _ => "other",
};

static string WithUnderscore(int n)
{
    const int _ = 0;
    return n switch
    {
        1 => "one",
        _ => "zero",
        var x => "other: " + x,
    };
}

end の例

11.2.8 型パターン

type_patternは、パターン入力値 (§11.1) に特定の型があることをテストするために使用されます。

type_pattern
    : type
    ;

型Tに名前を付ける型パターンは、がE (E) と互換性のあるすべての型Tに適用できます。

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

11.2.9 リレーショナル パターン

relational_patternは、パターン入力値 (§11.1) を定数値に対してリレーショナルにテストするために使用されます。

relational_pattern
    : '<'  relational_expression
    | '<=' relational_expression
    | '>'  relational_expression
    | '>=' relational_expression
    ;

定数値に評価するには、relational_pattern内のrelational_expressionが必要です。

リレーショナル パターンは、関係演算子の<をサポートします。 <=、>、>=、sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、列挙型の両方のオペランドを使用して、このような二項関係演算子をサポートするすべての組み込み型にdecimal、nint、およびnuintします。

relational_patternは、適切な組み込みの二項関係演算子が型の両方のオペランドで定義されている場合、または明示的な null 許容変換またはボックス化解除変換がTから定数式の型に存在する場合に、型TにTできます。

式が double.NaN、 float.NaN、または null 定数と評価された場合、コンパイル時エラーになります。

入力値に、適切な組み込みの二項関係演算子が定義されている型がある場合、その演算子の評価はリレーショナル パターンの意味と見なされます。 それ以外の場合、明示的な null 許容変換またはボックス化解除変換を使用して、入力値が定数式の型に変換されます。 そのような変換が存在しない場合、コンパイル時エラーとなります。 変換が失敗した場合、パターンは一致しないと見なされます。 変換が成功した場合、パターン マッチング操作の結果は、変換された入力 e «op» v 式を評価した結果です。ここで、 e は変換された入力、«op» は関係演算子、 v は定数式です。

例:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(double.NaN));
Console.WriteLine(Classify(2.4));

static string Classify(double measurement) => measurement switch
{
    < -4.0 => "Too low",
    > 10.0 => "Too high",
    double.NaN => "Unknown",
    _ => "Acceptable",
};

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

Too high
Unknown
Acceptable

end の例

11.2.10 論理パターン

logical_patternは、パターン一致の結果を否定したり、結合 (and) または結合 (or) を使用して複数のパターン一致の結果を結合したりするために使用されます。

logical_pattern
    : disjunctive_pattern
    ;

disjunctive_pattern
    : disjunctive_pattern 'or' conjunctive_pattern
    | conjunctive_pattern
    ;

conjunctive_pattern
    : conjunctive_pattern 'and' negated_pattern
    | negated_pattern
    ;

negated_pattern
    : 'not' negated_pattern
    | primary_pattern
    ;

not、 and、および or は、まとめて パターン演算子と呼ばれます。

negated_patternは、否定されるパターンが一致しない場合に一致し、その逆も一致します。 conjunctive_patternでは、両方のパターンが一致する必要があります。 disjunctive_patternには、いずれかのパターンが一致する必要があります。 対応する言語演算子、 && 、 ||とは異なり、 and と or はショートサーキット演算子 ではありません 。

これは、パターン変数が not または or パターン演算子の下で宣言されるコンパイル時エラーです。

注: not も or もパターン変数に対して明確な代入を生成できないため、これらの位置で宣言するのはエラーです。 注釈

conjunctive_patternでは、2 番目のパターンの入力型は、の最初のパターンのandまれます。 パターン のPは、次のように定義されます。

  • Pが型パターンの場合、縮小された型は型パターンの型です。
  • それ以外の場合、 P が宣言パターンの場合、 縮小された型 は宣言パターンの型です。
  • それ以外の場合、 P が明示的な型を提供する再帰パターンである場合、 縮小された型はその型 になります。
  • それ以外の場合、Pがpositional_pattern (ITuple) のの規則を使用して照合される場合、縮小された型は型System.ITuple。
  • それ以外の場合、Pが定数が null 定数ではなく、式が入力型への定数式変換を持たない定数パターンの場合、縮小された型は定数の型になります。
  • それ以外の場合、Pが、定数式が入力型への定数式変換を持たないリレーショナル パターンの場合、縮小された型は定数の型になります。
  • それ以外の場合、 P が or パターンの場合、 このような共通型 が存在する場合、 縮小された型 はサブパターンの縮小型の共通型になります。 このため、共通型アルゴリズムでは、ID、ボックス化、および暗黙的な参照変換のみが考慮され、 or パターンのシーケンスのすべてのサブパターンが考慮されます (かっこで保護されたパターンは無視されます)。
  • それ以外の場合、 P が and パターンの場合、 縮小された型 は右のパターンの 狭い型 になります。 また、左パターンの 狭いタイプ は、右パターンの 入力タイプ である。
  • それ以外の場合、PはPの入力型です。

注: 文法で示されているように、 not は andよりも優先され、 orよりも優先されます。 これは、かっこを使用して明示的に指定またはオーバーライドできます。 注釈

パターンがisの右側に表示される場合、パターンの範囲は文法によって決まります。その結果、パターン演算子and、or、およびパターン内のnotは、パターンの外側の論理演算子&&、||、および!よりも厳密にバインドされます。

例:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(-100));
Console.WriteLine(Classify(5.7));

static string Classify(double measurement) => measurement switch
{
    < -40.0 => "Too low",
    >= -40.0 and < 0 => "Low",
    >= 0 and < 10.0 => "Acceptable",
    >= 10.0 and < 20.0 => "High",
    >= 20.0 => "Too high",
    double.NaN => "Unknown",
};

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

High
Too low
Acceptable

end の例

例:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    3 or 4 or 5 => "spring",
    6 or 7 or 8 => "summer",
    9 or 10 or 11 => "autumn",
    12 or 1 or 2 => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date),
      $"Date with unexpected month: {date.Month}."),
};

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

winter
autumn
spring

end の例

例:

object msg = "msg";
object obj = 5;
bool flag = true;

// This is parsed as: (msg is (not int) or string)
bool result = msg is not int or string;
Console.WriteLine($"msg (\"msg\"): msg is not int or string: {result}");

// This is parsed as: (obj is (int or string)) && flag
result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int || obj is string && flag: {result}");

flag = false;
// This is parsed as: (obj is (int or string)) && flag
result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}");

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

msg ("msg"): msg is not int or string: True
obj (5), flag (true): obj is int or string && flag: True
obj (5), flag (true): obj is int || obj is string && flag: True
obj (5), flag (false): obj is int or string && flag: False
obj (5), flag (false): obj is int || obj is string && flag: True

end の例

11.2.11 リスト パターン

list_patternは、リストまたは配列内の要素のシーケンスと一致します。

list_pattern
    : list_pattern_clause simple_designation?
    ;

list_pattern_clause
    : '[' (pattern (',' pattern)* ','?)? ']'
    ;

list_patternは、カウント可能な任意の型 (§18.1) およびインデックス可能な (§18.1) と互換性があります。これには、引数としてIndexを受け取るアクセス可能なインデクサー、または 1 つのint パラメーターを持つアクセス可能なインデクサーがあります。 両方のインデクサーが存在する場合は、前者が推奨されます。 (暗黙的なインデックスのサポートの詳細については、 §18.4.2 を参照してください)。

expr is [1, 2, 3]形式のパターンは、次のコードと同じです。

expr.Length is 3
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Index(1, fromEnd: false)] is 2
&& expr[new Index(2, fromEnd: false)] is 3

例:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3 and not 7]);  // True

end の例

破棄パターン (§11.2.7) は、任意の 1 つの要素に一致します。

例:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [_, var second, _])
{
    Console.WriteLine($"The second element is {second}.");
}

end の例

11.2.12 スライス パターン

slice_patternは 0 個以上の要素を破棄します。 これは、 list_pattern_clause内でのみ直接使用され、その句では最大で 1 回のみ使用されます。

slice_pattern
    : '..' pattern?
    ;

サブパターンのない slice_pattern は、 list_patternと互換性のある任意の型と互換性があります。 サブパターンを持つslice_patternは、カウント可能な任意の型 (§18.1) およびスライス可能な (§18.1) と互換性があります。これには、引数としてRangeを受け取るアクセス可能なインデクサー、または 2 つのSlice パラメーターを持つアクセス可能なint メソッドがあります。 両方が存在する場合は、前者が好ましい。 (暗黙的なインデックスのサポートの詳細については、 §18.4.2 を参照してください)。

slice_patternは適切な破棄のように動作します。つまり、そのようなパターンに対するテストは行われません。 代わりに、他のノード (つまり、長さとインデクサー) にのみ影響します。 たとえば、フォーム expr is [1, .. var s, 3] のパターンは、次のコードと同じです (明示的な Index と Range サポートによって互換性がある場合)。

expr.Length is >= 2
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))] is var s
&& expr[new Index(1, fromEnd: true)] is 3

slice_patternの入力型は、基になるthis[Range]または Slice メソッドの戻り値の型であり、2 つの例外があります。stringと配列の場合は、それぞれstring.SubstringとRuntimeHelpers.GetSubArrayを使用します。

例: スライス パターンは、入力シーケンスの先頭または末尾でのみ要素を照合するために使用できます。

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

end の例

例: サブパターンは、スライス パターン内で入れ子にすることができます。

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron"); // output: Message apron doesn't match.

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });     // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0]
        ? "valid" : "not valid";
    Console.WriteLine(result);
}

end の例