11 Шаблонов и сопоставления шаблонов

11.1 Общие

Шаблон можно использовать с оператором is (§12.15.12), в switch_statement (§13.8.3) и в switch_expression (§12.12), чтобы описать форму данных, с которой сравниваются входящие данные. Шаблоны могут быть вложенными, с частями данных, которые сопоставляются с вложенными шаблонами.

Шаблон проверяется на соответствие значению в ряде контекстов:

  • В switch_statementшаблонswitch_label проверяется на selector_expressionswitch_statement.
  • При использовании оператора is-patternшаблон справа проверяется на выражение слева.
  • В switch_expressionшаблонswitch_expression_arm проверяется на выражение в левой части switch_expression.
  • В вложенных контекстах вложенный шаблон проверяется на значения, полученные из свойств, полей или индексированных из других входных значений в зависимости от формы шаблона.

Значение, по которому проверяется шаблон, называется входным значением шаблона.

Шаблон Pподмечен набором незащищенных шаблонов Q , если любое входное значение, соответствующее P одному из элементов Q.

В инструкции switch (§13.8.3) возникает ошибка, если шаблон дела подмечен предыдущим набором незащищенных случаев (§13.8.3). В выражении коммутатора (§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_patterns.

Если входные данные можно синтаксически распознать как constant_pattern , так и positional_pattern , то будет выбрана constant_pattern .

Некоторые шаблонымогут привести к объявлению локальной переменной.

Каждая форма шаблона определяет набор типов входных значений, к которым может применяться шаблон. Шаблон P применяется к типу T , если T относится к типам, значения которых могут соответствовать шаблону. Это ошибка во время компиляции, если шаблон отображается в программе для сопоставления входного значения шаблона P (§11.1) типа 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
}

пример конца

Каждая форма шаблона определяет набор значений, для которых шаблон соответствует значению во время выполнения.

Порядок оценки операций и побочных эффектов во время сопоставления шаблонов (вызовы 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
    ;

При признании simple_designation , если применяются discard_designation иsingle_variable_designation альтернативные варианты, то следует выбрать прежнее.

Примечание. ANTLR автоматически делает указанный выбор из-за упорядочения альтернатив simple_designation. конечная заметка

Это ошибка во время компиляции, если тип является типом значения null (§8.3.12) или типом ссылки, допускающей значение NULL (§8.9.3).

Тип среды выполнения значения проверяется на тип в шаблоне, используя те же правила, указанные в операторе is-type (§12.15.12.1). Если тест выполнен успешно, шаблон соответствует указанному значению.

Примечание. Выражение 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) заданного типа, именуемого заданным идентификатором. Эта локальная переменная назначается значение входного значения шаблона, если шаблон соответствует значению.

Примечание. Это лечение _ внутри declaration_pattern отличается от того, _ что автономное письмо, написанное как шаблон (§11.2.7): в последнем случае константа области или тип с именем _, если таковые имеются, не скрыты. конечная заметка

Как сообщается, тип Eсовместим с типом T , если существует преобразование удостоверений, неявное или явное преобразование ссылок, преобразование бокса, преобразование распаковки или неявное или явное преобразование ETтипа значений, допускающего значение NULL, или если ET это открытый тип (§8.4.3). Шаблон объявления, именующий тип T, применим к (§11.2.1), для которого EE совместим Tшаблон. Это ошибка во время компиляции, если шаблон объявления, именующий тип T , используется для сопоставления входного значения шаблона (§11.1), статический тип E которого несовместим с Tшаблоном.

Примечание. Поддержка открытых типов может оказаться наиболее полезной при проверке типов, которые могут быть либо структурой, либо типами классов, а бокс следует избежать. конечная заметка

Пример. Шаблон объявления полезен для выполнения тестов типов времени выполнения ссылочных типов и заменяет идиом

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

с немного более кратким

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

пример конца

Пример. Шаблон объявления можно использовать для проверки значений типов, допускающих значение NULL: значение типа (или поляNullable<T>) соответствует шаблону T типа T2 id , если значение не равно null и T2 имеет Tзначение, или какой-либо базовый тип или интерфейсT. Например, в фрагменте кода

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

Условие инструкции if находится true во время выполнения, а переменная v содержит значение 3 типа int внутри блока. После блокировки переменная v находится в области, но не определенно назначена. пример конца

11.2.3 Константный шаблон

Constant_pattern используется для проверки значения входного значения шаблона (§11.1) с заданным константным значением.

constant_pattern
    : constant_expression
    ;

Шаблон P константы применим к типу, если имеется неявное преобразование из константного выражения P типа в типTT, или если System.Span<char>T выражение является или System.ReadOnlySpan<char> константным выражением P типа string и не null является литеральным.

Для константного шаблона Pпреобразованное значение равно

  • Если тип входного значения шаблона является целочисленным или типом перечисления, значение константы шаблона преобразуется в этот тип; иначе
  • Если тип входного значения шаблона — это версия целочисленного типа или типа перечисления, константное значение шаблона, преобразованное в его базовый тип; иначе
  • значение константы шаблона.

Учитывая входное значение шаблона e и константный шаблон P с преобразованным значением v,

  • Значение e, если имеет целочисленный тип или тип перечисления, или пустую форму одного из них, и v имеет целочисленный тип, шаблон P соответствует значению e, если результат выражения e == v имеет trueзначение ; в противном случае
  • Значение <a0/>, если e имеет тип или v является константной строкой, и v не имеет константного значения, шаблон соответствует значению e, если возвращается; в противном случае
  • Шаблон P соответствует значению e, если возвращаетсяobject.Equals(e, v).true

Пример. Оператор switch в следующем методе использует пять постоянных шаблонов в метках регистра.

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(...);
    }
}

пример конца

Шаблон Var 11.2.4

Var_pattern соответствует каждому значению. То есть операция сопоставления шаблонов с 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 привязывается к типу, в котором используется var_pattern .

Если обозначение является tuple_designation, шаблон эквивалентен positional_pattern (§11.2.5) (var формы, ... ) где обозначениянаходятся в tuple_designation. Например, шаблон var (x, (y, z)) эквивалентен (var x, (var y, var z)).

11.2.5 Позиционный шаблон

Positional_pattern проверяет, не nullявляется ли входное значение, извлекает из него последовательность значений и сопоставляет каждое извлеченное значение с соответствующим подпаттерном. Значения извлекаются одним из трех способов: рассматривая входные данные как кортеж, вызывая 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 не равно arity этого типа кортежа. Во время выполнения каждый элемент кортежа соответствует соответствующему подпаттерну; Совпадение завершается успешно, если все из них успешно. Если в подпаттерне есть идентификатор, этот идентификатор должен назвать элемент кортежа в соответствующей позиции в типе кортежа.
  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ссылок, применяется этот случай. Во время выполнения входное значение проверяется для того, чтобы он не был экземпляромnullSystem.Runtime.CompilerServices.ITuple; если этот тест завершается ошибкой, совпадение позиционного шаблона завершается ошибкой. В противном случае свойство значения Length считывается и, если оно не равно n, совпадение позиционного шаблона завершается ошибкой. В противном случае для каждого значения i от 1 до n значение, полученное путем индексирования входного значения с i – 1, совпадает с подпаттерномi-th, и совпадение успешно, если все из них успешно.
  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);
}

пример конца

Пример. Имена элементов кортежа и деконструкционных параметров можно использовать в позиционной схеме следующим образом:

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

пример конца

Шаблон свойств 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 сподпаттернами{типа} шаблона, это ошибка во время компиляции, если выражение e несовместимо с шаблоном (§11.2.2) с типомT, указанным по типу. Если тип отсутствует, предполагается, что тип является статическим типом e. Каждая subpattern_name, отображаемая в левой части его подпаттернов, должна назначить доступное для чтения свойство или поле T. Если simple_designation property_pattern присутствует, он объявляет переменную шаблона типа T.

Во время выполнения выражение проверяется на T. Если это не удается, совпадение шаблона свойства завершается ошибкой, и результатом является false. В случае успешного выполнения каждое property_subpattern поле или свойство считывается, а его значение соответствует соответствующему шаблону. Результат всего совпадения заключается false только в том случае, если результат любого из них false. Порядок сопоставления подпаттернов не указан, и не удалось проверить все подпаттерны во время выполнения. Если совпадение выполнено успешно, и simple_designationproperty_pattern является single_variable_designation, объявленная переменная назначается соответствующее значение.

Property_pattern можно использовать для сопоставления шаблонов с анонимными типами.

Subpattern_name может ссылать на вложенный элемент. В таком случае получатель для каждого подстановки имени является типом предыдущего члена T₀, начиная с типа входных данных property_pattern. Если T является типом, допускаемым значением NULL, T₀ является его базовым типом, в противном случае T₀ равен T. Например, шаблон формы { Prop1.Prop2: pattern } точно эквивалентен { Prop1: { Prop2: pattern } }.

Примечание. Это будет включать проверку null, если T является типом значений, допускаемым значением NULL, или ссылочным типом. Эта проверка null означает, что доступные вложенные свойства будут свойствами T₀, а не T. Так как разрешены повторяющиеся пути элементов, компиляция сопоставления шаблонов может воспользоваться общими частями шаблонов. конечная заметка

Пример:

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

пример конца

Пример: проверка типа выполнения и объявление переменной можно добавить в шаблон свойств следующим образом:

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

пример конца

Шаблон отмены 11.2.7

Каждое выражение соответствует шаблону отмены, что приводит к отмене значения выражения.

discard_pattern
    : '_'
    ;

Где синтаксический контекст разрешает шаблон, если маркер _ будет разрешаться как simple_name (§12.8.4) к доступной константе или типу, то _он не рассматривается как discard_pattern. Вместо:

  • При _ разрешении на доступную константу интерпретируется как _ (§11.2.3), константное выражение которого является этой константой.
  • При _ разрешении типа в правой части is оператора конструктор relational_expressionis _ интерпретируется как оператор is-type (§12.15.12.1) для этого типа. В любом другом синтаксическом контексте, допускающего шаблон, голый _ разрешение типа не является допустимым шаблоном. Однако может отображаться как _declaration_pattern (например, _ x ) или в других формах шаблонов, которые явно называют тип.

Это правило сохраняет обратную совместимость с кодом, определенным _ как тип или идентификатор до внедрения шаблона отмены. Если _ разрешено что-либо другое, кроме доступной константы или типа (например, локальная переменная, параметр, поле или метод), правило не применяется и _ остается discard_pattern.

Примечание. Это аналогично правилу var в §11.2.4, за исключением того, что для _ константы или типа _ области может быть интерпретировано как ссылка на это объявление, а не создание ошибки. конечная заметка

_ Если после применения предыдущего правила маркер по-прежнему является discard_pattern, это ошибка во время компиляции для этого discard_pattern отображаться как весь шаблон relational_expressionшаблона формы is или как весь шаблонswitch_label. Однако discard_pattern может отображаться как подпаттерн включающего шаблона (например, как подпаттернpositional_pattern или property_pattern).

Примечание. В этих случаях для сопоставления любого выражения используйте var_pattern с отменой var _. конечная заметка

Пример:

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 выражение обрабатывает все возможные входные значения. пример конца

Пример. Ниже показано, как константа области с именем _ изменяет интерпретацию _ руки в switch выражении. В WithoutUnderscore, _ рука является discard_pattern и соответствует любому значению. В WithUnderscoreобласти константы __ строка интерпретируется как constant_pattern , которая соответствует только значению 0.

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,
    };
}

пример конца

Шаблон типа 11.2.8

Type_pattern используется для проверки того, что входное значение шаблона (§11.1) имеет заданный тип.

type_pattern
    : type
    ;

Шаблон типа, именующий типT, применим к каждому типуE, для E которого совместим T (§11.2.2).

Тип среды выполнения проверяется на тип с использованием одинаковых правил, указанных в операторе 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_expression в relational_pattern .

Реляционные шаблоны поддерживают реляционные операторы <, <=>и >= все встроенные типы, поддерживающие такие двоичные реляционные операторы с обоими операндами с одинаковым типом: sbyte, byteshortushortintuintlongulongcharfloatdoubledecimalnintи nuintперечисления.

Relational_patternприменимо к типуT, если подходящий встроенный двоичный реляционный оператор определен с обоими операндами типаT, или если явное преобразование null или распаковки существует из T типа константного выражения.

Это ошибка во время компиляции, если выражение оценивается как double.NaNfloat.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

пример конца

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.

Примечание. Поскольку ни один из них не notor может создавать определенное назначение для переменной шаблона, это ошибка объявления одной из этих позиций. конечная заметка

В conjunctive_patternвходной тип второго шаблона сужается типом сужающих требований первого шаблона and. Узкий тип шаблона P определяется следующим образом:

  • Если P это шаблон типа, узкий тип является типом типа шаблона типа.
  • В противном случае, если P это шаблон объявления, узкий тип является типом типа шаблона объявления.
  • В противном случае, если P это рекурсивный шаблон, предоставляющий явный тип, то узкий тип — это тип.
  • В противном случае, если P они соответствуют правилам ITuple в positional_pattern (§11.2.5), то узкий тип — это тип System.ITuple.
  • В противном случае, если P является константой, в которой константы не являются пустой константой и где выражение не имеет преобразования констант в входной тип, то узкий тип является типом константы.
  • В противном случае, если P это реляционный шаблон, в котором константное выражение не имеет преобразования константного выражения в входной тип, то узкий тип является типом константы.
  • В противном случае, если P это or шаблон, узкий тип является общим типом узкого типа подпаттернов, если такой распространенный тип существует. Для этого алгоритм общего типа учитывает только удостоверения, боксы и неявные преобразования ссылок, а также рассматривает все подпаттерны последовательности or шаблонов (игнорируя круглые скобки).
  • В противном случае, если P это шаблон, andтип является узким типом правильного шаблона. Кроме того, узкий тип левого шаблона является типом входных данных правого шаблона.
  • В противном случае узкийP тип — Pвходной тип.

Примечание. Как указано грамматикой, not имеет приоритет над and, который имеет приоритет над or. Это можно явно указать или переопределить с помощью круглых скобок. конечная заметка

Когда шаблон отображается справа isот справа, степень шаблона определяется грамматикой; в результате операторы шаблонов andorи 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

пример конца

Пример:

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

пример конца

Пример:

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

пример конца

Шаблон списка 11.2.11

List_pattern соответствует последовательности элементов в списке или массиве.

list_pattern
    : list_pattern_clause simple_designation?
    ;

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

List_pattern совместим с любым типом, который можно считать (§18.1), а также индексируемым (§18.1)— он имеет доступный индексатор, который принимает в качестве аргумента или доступный индексатор с одним Indexint параметром. Если оба индексаторов присутствуют, то он предпочтителен. (Дополнительные сведения о поддержке неявного индекса см. в разделе §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

пример конца

Шаблон отмены (§11.2.7) соответствует любому одному элементу.

Пример:

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

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

пример конца

Шаблон среза 11.2.12

Slice_pattern удаляет ноль или больше элементов. Он должен использоваться только непосредственно в list_pattern_clause, а затем только один раз в этом предложении.

slice_pattern
    : '..' pattern?
    ;

Slice_pattern без подпаттерна совместим с любым типом, совместимым с list_pattern. Slice_pattern с подпаттерном совместим с любым типом, который можно считать (§18.1), а также срезаемым (§18.1) — он имеет доступный индексатор, который принимает Range в качестве аргумента или доступный 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 метода с двумя исключениями: для 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

пример конца

Пример: подпаттерн может быть вложен в шаблон среза:

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);
}

пример конца