Поделиться через


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

11.1 Общие

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

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

  • В инструкции switch шаблон метки регистра проверяется в выражении инструкции switch.
  • В операторе is-patternшаблон справа проверяется с выражением слева.
  • В выражении коммутатора шаблонswitch_expression_arm проверяется на выражение в левой части выражения switch-expression.
  • В вложенных контекстах вложенный шаблон проверяется на значения, полученные из свойств, полей или индексированных из других входных значений в зависимости от формы шаблона.

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

Формы шаблонов 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
    ;

parenthesized_pattern
    : '(' pattern ')'
    ;

'(' pattern ')' Рабочая среда позволяет заключать шаблон в скобки, чтобы обеспечить порядок оценки между шаблонами, объединенными с помощью одного из logical_patterns.

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

Каждая форма шаблона определяет набор типов входных значений, к которым может применяться шаблон. Шаблон 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.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.

Тип среды выполнения значения проверяется на тип в шаблоне, используя те же правила, указанные в операторе is-type (§12.15.12.1). Если тест выполнен успешно, шаблон соответствует указанному значению. Это ошибка во время компиляции, если тип является типом значения null (§8.3.12) или типом ссылки, допускающей значение NULL (§8.9.3). Эта форма шаблона никогда не соответствует значению null .

Примечание. Выражение e is T is-type и шаблон e is T _ объявления эквивалентны, если T не является типом NULL. конечная заметка

Учитывая входное значение шаблона (§11.1) e, если simple_designationdiscard_designation, он обозначает отмену (§9.2.9.2), а значение e не привязано ни к чему. (Хотя объявленная переменная с именем _ может находиться в области в этой точке, эта именованной переменной не рассматривается в этом контексте.) В противном случае, если simple_designation single_variable_designation, вводятся локальная переменная (§9.2.9) заданного типа, именуемого заданным идентификатором. Эта локальная переменная назначается значение входного значения шаблона, если шаблон соответствует значению.

Некоторые сочетания статического типа входного значения шаблона и заданного типа считаются несовместимыми и приводят к ошибке во время компиляции. Значение статического типа E , как утверждается, совместимо с типом T , если существует преобразование удостоверений, неявное или явное преобразование ссылок, преобразование бокса, преобразование распаковки или неявное или явное преобразование типа ETзначений, допускающего значение NULL, или ET если либо или является открытым типом (§8.4.3). Шаблон объявления, именующий тип T , применим к каждому типу E , для которого 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преобразованное значение равно

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

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

  • Значение e, если имеет целочисленный тип или тип перечисления, или пустую форму одного из них, и v имеет целочисленный тип, шаблон P соответствует значению e, если результат выражения e == v имеет trueзначение ; в противном случае
  • Шаблон 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 метод (§12.7) и выполняет дальнейшее сопоставление шаблонов в результирующих значениях. Он также поддерживает синтаксис шаблонов, аналогичный кортежам (без предоставленного типа), если тип входного значения совпадает с типом, содержащим Deconstruct, или если тип входного значения является типом кортежа, или если тип входного значения является object или System.ITuple типом среды выполнения выражения System.ITuple.

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

Учитывая совпадение входного значения сподпаттернами(типа) шаблона, метод выбирается путем поиска в типе объявлений для доступных Deconstruct объявлений и выбора одного из них с использованием одних и того же правила, что и для объявления деконструкции. Это ошибка, если positional_pattern окупает тип, имеет один подпаттерн без идентификатора, не имеет property_subpattern и не имеет simple_designation. Это отличается от constant_pattern , которое скобок и positional_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);
}

пример конца

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

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

пример конца

Шаблон свойств 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 {}) ...

конечная заметка

Учитывая совпадение выражения e сподпаттернами{типа} шаблона, это ошибка во время компиляции, если выражение e не совместимо с типом T, указанным по типу. Если тип отсутствует, предполагается, что тип является статическим типом e. Каждый идентификатор, отображаемый в левой части его подпаттернов, должен назначить доступное для чтения свойство или поле T. Если simple_designation property_pattern присутствует, он объявляет переменную шаблона типа T.

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

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

Пример:

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

Это ошибка во время компиляции для использования шаблона отмены в relational_expression формы relational_expressionisили в качестве шаблона switch_label.

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

Шаблон типа 11.2.8

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

type_pattern
    : type
    ;

Шаблон типа, именующий типT, применим к каждому типуE, для T которого E совместим шаблон (§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, longdecimalintushortuintulongshortbytefloatdoublecharnintи 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являются короткими операторами.

Это ошибка во время компиляции для переменной шаблона, объявленной под оператором шаблона или or шаблономnot.

Примечание. Поскольку ни один из них не 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)
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
bool 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.3 Подсемпция шаблона

В инструкции switch это ошибка, если шаблон дела подмечен предыдущим набором незащищенных случаев (§13.8.3). В неофициальном порядке это означает, что любое входное значение было бы сопоставлено одним из предыдущих случаев. Следующие правила определяют, когда набор шаблонов подмножет заданный шаблон:

Шаблон Pбудет соответствовать константе K , если какое-либо из следующих условий хранится:

  • Спецификация поведения среды выполнения этого шаблона соответствует PK.
  • Pявляется type_pattern для типа T и не null является, а K тип K среды выполнения является T или типом, производным от T или типом, реализующимT.
  • Pэто relational_pattern с оператором "op" и константой v, а выражение K "op" v будет оцениваться true.
  • P является negated_patternnot P₁ и P₁ не соответствует K.
  • P — это conjunctive_patternP₁ and P₂ , и оба P₁ совпадают K и P₂ будут соответствовать K.
  • Pэто disjunctive_patternP₁ or P₂ и P₁ либо совпадать K , либо P₂ совпадать K.
  • P — это discard_pattern.

Набор шаблонов Qподменимирует шаблон P , если какое-либо из следующих условий хранится:

  • P— это константный шаблон, и любой из шаблонов в наборе Q будет соответствовать Pпреобразованном значению.
  • P— это шаблон var, и набор шаблонов Q является исчерпывающим.
  • P— это шаблон объявления с типом, и набор шаблонов T является Q для типа T (§11.4).
  • P — это type_pattern для типа T , и набор шаблонов Qявляется исчерпывающим для типа T.
  • P — это relational_pattern с оператором "op" и константным значением v, а для каждого значения входного типа, удовлетворяющего отношению "op" v, некоторые шаблоны в наборе Q будут соответствовать значению.
  • P— это disjunctive_patternP₁ or P₂ и Q набор подсуменов P₁P₂шаблоновQ.
  • P — это conjunctive_patternP₁ and P₂ и по крайней мере один из следующих удержаний: Q подсумы P₁или Q вложенные P₂элементы.
  • P является negated_patternnot P₁ и Qявляется исчерпывающим для входного типа, учитывая только значения, не соответствующие P₁.
  • Pэто discard_pattern , и набор шаблонов Qявляется исчерпывающим для типа входного значения шаблона, а входное значение шаблона не является типом null или каким-либо шаблоном в Q сопоставлении null.
  • Некоторые шаблоны в Q этом случае — это disjunctive_patternQ₁ or Q₂ и замена этого шаблона Q₁ в Q наборе, который подсумывает P, или заменять его Q₂ набором, который подсумывает P.
  • Некоторые шаблоны являются Qnegated_patternnot Q₁ и P не соответствуют ни одному значению, которое Q₁ будет соответствовать.

11.4. Исчерпывающая схема

Неформальным образом набор шаблонов является исчерпывающим для типа, если для каждого возможного значения этого типа, отличного от NULL, применяется некоторый шаблон в наборе. Следующие правила определяют, когда набор шаблонов является исчерпывающим для типа:

Набор шаблонов Q является исчерпывающим:

  1. T является целочисленным или типом перечисления или версией, допускающей значение NULL, и для каждого из возможных значений Tбазового типа, не допускающего значения NULL, некоторые шаблоны будут Q соответствовать значению этого значения; или
  2. Некоторые шаблоны в Q шаблоне var; или
  3. Некоторые шаблоны являются Qшаблоном объявления для типа D, и существует преобразование удостоверений, неявное преобразование ссылок или преобразование бокса из TD; или
  4. Некоторые шаблоны являются Qtype_pattern для типа D, и существует преобразование удостоверений, неявное преобразование ссылок или преобразование бокса из TD; или
  5. В некоторых шаблонах Q используется discard_pattern; или
  6. Шаблоны включают Q сочетание relational_patternи constant_pattern, диапазоны которых коллективно охватывают все возможные значения Tбазового типа, не допускающего значения NULL. Для float и double типов это включает System.Double.NaN или System.Single.NaN соответственно, так как NaN не соответствует ни одному реляционному шаблону;
  7. Некоторые шаблоны являются Qdisjunctive_patternP₁ or P₂ и заменяют этот шаблон как P₁ в том, так и P₂ в Q дает набор, который является исчерпывающим для T; или
  8. Некоторые шаблоны являются Qnegated_patternnot P₁, а шаблоны вместе Q со значениями, не соответствующими P₁ каждому возможному Tзначению. Negated_patternnot P₁ является исчерпывающим сам по себе, если P₁ не соответствует возможному Tзначению ; или
  9. Некоторые шаблоны являются Qconjunctive_patternP₁ and P₂, а набор, содержащий толькоP₁, является исчерпывающим, и набор, содержащий толькоP₂, являетсяисчерпывающимT.T

Примечание. Если шаблон типа включает типы, допускающие значение NULL, шаблон может быть исчерпывающим для типа, но по-прежнему создает предупреждение, так как шаблон типа не будет соответствовать значению null . конечная заметка

Примечание. Для типов с плавающей запятой сочетание шаблонов < 0 и >= 0не является исчерпывающим, так как ни реляционный шаблон не соответствует NaN. Правильный исчерпывающий набор будет < 0иметь значение , >= 0и double.NaN (или float.NaN). конечная заметка

Пример:

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

пример конца