Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
11.1 Geral
Um padrão é uma forma sintactica que pode ser usada com o is operador (§12.15.12), em um switch_statement (§13.8.3) e em um switch_expression (§12.12) para expressar a forma dos dados em relação aos quais os dados de entrada devem ser comparados. Os padrões podem ser recursivos, de modo que partes dos dados possam ser correspondidas com sub padrões.
Um padrão é testado em relação a um valor em vários contextos:
- Em uma instrução switch, o padrão de um rótulo de caso é testado em relação à expressão da instrução switch.
- Em um operador is-pattern , o padrão no lado direito é testado em relação à expressão à esquerda.
- Em uma expressão switch, o padrão de um switch_expression_arm é testado em relação à expressão no lado esquerdo da expressão de comutador.
- Em contextos aninhados, o sub padrão é testado em relação aos valores recuperados de propriedades, campos ou indexados de outros valores de entrada, dependendo do formulário padrão.
O valor no qual um padrão é testado é chamado de valor de entrada padrão.
11.2 Formas de padrão
11.2.1 Geral
Um padrão pode ter uma das seguintes formas:
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 ')'
;
A '(' pattern ')' produção permite que um padrão seja colocado entre parênteses para impor a ordem de avaliação entre os padrões combinados usando um dos logical_patterns.
Alguns padrõespodem resultar na declaração de uma variável local.
Cada formulário de padrão define o conjunto de tipos para valores de entrada aos quais o padrão pode ser aplicado. Um padrão P é aplicável a um tipo T se T estiver entre os tipos cujos valores o padrão pode corresponder. É um erro em tempo de compilação se um padrão P aparecer em um programa para corresponder a um valor de entrada de padrão (§11.1) do tipo T se P não for aplicável a T.
Exemplo: o exemplo a seguir gera um erro de tempo de compilação porque o tipo de tempo de
vcompilação de éTextReader. Uma variável do tipoTextReadernunca pode ter um valor compatível com referência comstring:TextReader v = Console.In; // compile-time type of 'v' is 'TextReader' if (v is string) // compile-time error { // code assuming v is a string }No entanto, o seguinte não gera um erro de tempo de compilação porque o tipo de tempo de
vcompilação éobject. Uma variável do tipoobjectpode ter um valor compatível com referência comstring:object v = Console.In; if (v is string s) { // code assuming v is a string }exemplo de fim
Cada formulário de padrão define o conjunto de valores para os quais o padrão corresponde ao valor em tempo de execução.
A ordem de avaliação de operações e efeitos colaterais durante a correspondência de padrões (chamadas para Deconstruct, acessos de propriedade e invocações de métodos em System.ITuple) não é especificada.
11.2.2 Padrão de declaração
Um declaration_pattern é usado para testar se um valor tem um determinado tipo e, se o teste for bem-sucedido, fornecer opcionalmente o valor em uma variável desse tipo.
declaration_pattern
: type simple_designation
;
simple_designation
: discard_designation
| single_variable_designation
;
discard_designation
: '_'
;
single_variable_designation
: identifier
;
Um simple_designation com o token _ deve ser considerado um discard_designation em vez de um single_variable_designation.
O tipo de runtime do valor é testado em relação ao tipo no padrão usando as mesmas regras especificadas no operador de tipo is (§12.15.12.1). Se o teste for bem-sucedido, o padrão corresponderá a esse valor. Será um erro de tempo de compilação se o tipo for um tipo de valor anulável (§8.3.12) ou um tipo de referência anulável (§8.9.3). Esse formulário de padrão nunca corresponde a um null valor.
Observação: a expressão
e is Tis-type e o padrãoe is T _de declaração são equivalentes quandoTnão é um tipo anulável. nota final
Dado um valor de entrada padrão (§11.1) e, se o simple_designation for discard_designation, ele indicará um descarte (§9.2.9.2) e o valor de e não estará associado a nada. (Embora uma variável declarada com o nome _ possa estar no escopo nesse ponto, essa variável nomeada não é vista neste contexto.) Caso contrário, se o simple_designation for single_variable_designation, uma variável local (§9.2.9) do tipo determinado nomeado pelo identificador especificado será introduzida. Essa variável local recebe o valor do valor de entrada do padrão quando o padrão corresponde ao valor.
Determinadas combinações de tipo estático do valor de entrada do padrão e do tipo fornecido são consideradas incompatíveis e resultam em um erro em tempo de compilação. Um valor de tipo E estático é considerado compatível com o padrão com o tipo T se houver uma conversão de identidade, uma conversão de referência implícita ou explícita, uma conversão de boxe, uma conversão de unboxing ou uma conversão de tipo de valor nulo implícita ou explícita de E para T, ou se ET for um tipo aberto (§8.4.3). Um padrão de declaração que nomeia um tipo T é aplicável a.
Observação: o suporte para tipos abertos pode ser mais útil ao verificar tipos que podem ser tipos struct ou class, e a conversão boxing deve ser evitada. nota final
Exemplo: o padrão de declaração é útil para executar testes de tipo de tempo de execução de tipos de referência e substitui o idioma
var v = expr as Type; if (v != null) { /* code using v */ }com o ligeiramente mais conciso
if (expr is Type v) { /* code using v */ }exemplo de fim
Exemplo: o padrão de declaração pode ser usado para testar valores de tipos anuláveis: um valor de tipo
Nullable<T>(ou um boxedT) corresponde a um padrãoT2 idde tipo se o valor não for nulo eT2forT, ou algum tipo base ou interface deT. Por exemplo, no fragmento de códigoint? x = 3; if (x is int v) { /* code using v */ }A condição da
ifinstrução estátrueem tempo de execução e a variávelvcontém o valor3do tipointdentro do bloco. Depois do bloco, a variávelvestá no escopo, mas não é atribuída definitivamente. exemplo de fim
11.2.3 Padrão constante
Um constant_pattern é usado para testar o valor de um valor de entrada de padrão (§11.1) em relação ao valor constante fornecido.
constant_pattern
: constant_expression
;
Um padrão P constante é aplicável a um tipo T se houver uma conversão implícita da expressão constante de P para o tipo T.
Para um padrão Pconstante , seu valor convertido é
- Se o tipo do valor de entrada do padrão for um tipo integral ou um tipo de enumeração, o valor constante do padrão será convertido nesse tipo; caso contrário
- Se o tipo do valor de entrada do padrão for a versão anulável de um tipo integral ou de um tipo de enumeração, o valor constante do padrão será convertido em seu tipo subjacente; caso contrário
- o valor do valor constante do padrão.
Dado um valor de entrada de padrão e e um padrão P constante com valor convertido v,
- Se E tiver tipo integral ou tipo enumeração, ou uma forma anulável de um desses, e V tiver tipo integral, o padrão
Pcorresponderá ao valor E se o resultado da expressãoe == vfortrue; caso contrário - O padrão
Pcorresponde ao valor e seobject.Equals(e, v)retornatrue.
Exemplo: a
switchinstrução no método a seguir usa cinco padrões constantes em seus rótulos de caso.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(...); } }exemplo de fim
11.2.4 Padrão Var
Um var_patterncorresponde a todos os valores. Ou seja, uma operação de correspondência de padrões com um var_pattern sempre é bem-sucedida.
Um var_pattern é aplicável a todos os tipos.
var_pattern
: 'var' designation
;
designation
: simple_designation
| tuple_designation
;
tuple_designation
: '(' designations? ')'
;
designations
: designation (',' designation)*
;
Dado um valor de entrada padrão (§11.1) e, se a designação for discard_designation, ele indicará um descarte (§9.2.9.2) e o valor de e não estará associado a nada. (Embora uma variável declarada com esse nome possa estar no escopo nesse ponto, essa variável nomeada não é vista neste contexto.) Caso contrário, se a designação for single_variable_designation, em runtime, o valor de e será associado a uma variável local recém-introduzida (§9.2.9) desse nome cujo tipo é o tipo estático de e e o valor de entrada padrão será atribuído a essa variável local.
É um erro se o nome var for associado a um tipo em que um var_pattern é usado.
Se a designação for um tuple_designation, o padrão será equivalente a um positional_pattern (§11.2.5) da (var do formulário, ...
) em que as designaçõessão aquelas encontradas no tuple_designation. Por exemplo, o padrão var (x, (y, z)) é equivalente a (var x, (var y, var z)).
Padrão posicional 11.2.5
Um positional_pattern verifica se o valor de entrada não nullé, invoca um método apropriado Deconstruct (§12.7) e executa uma correspondência de padrões adicional nos valores resultantes. Ele também dá suporte a uma sintaxe de padrão semelhante a tupla (sem o tipo que está sendo fornecido) quando o tipo do valor de entrada é o mesmo que o tipo que contém Deconstruct, ou se o tipo do valor de entrada é um tipo de tupla ou se o tipo do valor de entrada é object ou System.ITuple e o tipo de runtime da expressão implementa System.ITuple.
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern (',' subpattern)*
;
subpattern
: pattern
| identifier ':' pattern
;
Considerando uma correspondência de um valor de entrada para ossubpadrões( de tipo) de padrão, um método é selecionado pesquisando no tipo declarações acessíveis e Deconstruct selecionando uma entre elas usando as mesmas regras da declaração de desconstrução.
É um erro se um positional_pattern omitir o tipo, tiver um único subpadrão sem um identificador, não tiver property_subpattern e não tiver simple_designation. Isso desambigua entre uma constant_pattern parêntese e uma positional_pattern.
Para extrair os valores a serem correspondentes aos padrões na lista,
- Se o tipo for omitido e o tipo da expressão de entrada for um tipo de tupla, o número de subpadrões deverá ser o mesmo que a cardinalidade da tupla. Cada elemento de tupla é correspondido com o subpadrão correspondente e a correspondência é bem-sucedida se todas elas forem bem-sucedidas. Se qualquer subpadrão tiver um identificador, isso nomeará um elemento de tupla na posição correspondente no tipo de tupla.
- Caso contrário, se houver um adequado
Deconstructcomo um membro do tipo, será um erro de tempo de compilação se o tipo do valor de entrada não for compatível com o tipo. Em runtime, o valor de entrada é testado em relação ao tipo. Se isso falhar, a correspondência de padrão posicional falhará. Se for bem-sucedido, o valor de entrada será convertido nesse tipo eDeconstructserá invocado com variáveis geradas pelo compilador para receber os parâmetros de saída. Cada valor recebido é correspondido com o subpadrão correspondente e a correspondência é bem-sucedida se todas elas forem bem-sucedidas. Se qualquer subpadrão tiver um identificador, isso nomeará um parâmetro na posição correspondente deDeconstruct. - Caso contrário, se o tipo for omitido e o valor de entrada for de tipo
objectou algum tipo que possa ser convertidoSystem.ITuplepor uma conversão de referência implícita e nenhum identificador aparecer entre os subpadrões, a correspondência usaráSystem.ITuple. - Caso contrário, o padrão é um erro de tempo de compilação.
A ordem na qual os subpadrões são correspondidos no runtime não é especificada e uma correspondência com falha pode não tentar corresponder a todos os subpadrões.
Exemplo: aqui, desconstruimos um resultado de expressão e correspondemos aos valores resultantes com os padrões aninhados correspondentes:
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); }exemplo de fim
Exemplo: os nomes de elementos de tupla e parâmetros de desconstrução podem ser usados em um padrão posicional, da seguinte maneira:
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); }A saída produzida é
Sum of [10 20 30] is 60exemplo de fim
Padrão de propriedade 11.2.6
Um property_pattern verifica se o valor de entrada não nullé e corresponde recursivamente aos valores extraídos pelo uso de propriedades ou campos acessíveis.
property_pattern
: type? property_subpattern simple_designation?
;
property_subpattern
: '{' '}'
| '{' subpatterns ','? '}'
;
É um erro se qualquer subpadrão de um property_pattern não contiver um identificador.
Será um erro de tempo de compilação se o tipo for um tipo de valor anulável (§8.3.12) ou um tipo de referência anulável (§8.9.3).
Observação: um padrão de verificação nula sai de um padrão de propriedade trivial. Para verificar se a cadeia
sde caracteres não é nula, é possível gravar qualquer um dos seguintes formulários:#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 {}) ...nota final
Dada uma correspondência de uma expressão e com ossubpadrões{ de tipo} de padrão, será um erro de tempo de compilação se a expressão e não for compatível com padrão com o tipo T designado por tipo. Se o tipo estiver ausente, o tipo será considerado o tipo estático de e. Cada um dos identificadores que aparecem no lado esquerdo de seus subpadrões deve designar uma propriedade legível acessível ou um campo de T. Se o simple_designation do property_pattern estiver presente, ele declara uma variável padrão do tipo T.
Em runtime, a expressão é testada em relação a T. Se isso falhar, a correspondência do padrão de propriedade falhará e o resultado será false. Se for bem-sucedido, cada property_subpattern campo ou propriedade será lido e seu valor corresponderá ao padrão correspondente. O resultado de toda a correspondência será false somente se o resultado de qualquer um deles for false. A ordem na qual os subpadrões são correspondidos não é especificada e uma correspondência com falha pode não testar todos os subpadrões em runtime. Se a correspondência for bem-sucedida e o simple_designation do property_pattern for um single_variable_designation, a variável declarada será atribuída ao valor correspondente.
O property_pattern pode ser usado para correspondência de padrões com tipos anônimos.
Exemplo:
var o = ...; if (o is string { Length: 5 } s) ...exemplo de fim
Exemplo: uma verificação de tipo de tempo de execução e uma declaração de variável podem ser adicionadas a um padrão de propriedade, da seguinte maneira:
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."), };A saída produzida é
Hello Hi! 12345 abcexemplo de fim
Padrão de descarte 11.2.7
Cada expressão corresponde ao padrão de descarte, o que resulta no valor da expressão sendo descartada.
discard_pattern
: '_'
;
É um erro de tempo de compilação usar um padrão de descarte em um relational_expression dopadrãois de formulário ou como o padrão de um switch_label.
Observação: nesses casos, para corresponder a qualquer expressão, use um var_pattern com um descarte
var _. nota final
Exemplo:
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, };A saída produzida é
5.0 0.0 0.0Aqui, um padrão de descarte é usado para manipular
nulle qualquer valor inteiro que não tenha o membro correspondente daDayOfWeekenumeração. Isso garante que aswitchexpressão manipule todos os valores de entrada possíveis. exemplo de fim
Padrão de tipo 11.2.8
Um type_pattern é usado para testar se o valor de entrada padrão (§11.1) tem um determinado tipo.
type_pattern
: type
;
Um padrão de tipo que nomeia um tipo T é aplicável a todos os tipos E para os quais E o padrão é compatível com T (§11.2.2).
O tipo de runtime do valor é testado em relação ao tipo usando as mesmas regras especificadas no operador is-type (§12.15.12.1). Se o teste for bem-sucedido, o padrão corresponderá a esse valor. Será um erro em tempo de compilação se o tipo for um tipo que permite valor nulo. Esse formulário de padrão nunca corresponde a um null valor.
11.2.9 Padrão relacional
Um relational_pattern é usado para testar relacionalmente o valor de entrada padrão (§11.1) em relação a um valor constante.
relational_pattern
: '<' relational_expression
| '<=' relational_expression
| '>' relational_expression
| '>=' relational_expression
;
O relational_expression em um relational_pattern é necessário para ser avaliado como um valor constante.
Os padrões relacionais dão suporte aos operadores relacionais<, <=e >=>a todos os tipos internos que dão suporte a esses operadores relacionais binários com ambos os operandos com o mesmo tipo: sbyte, , byte, short, , ushortint, uint, long, , ulong, char, float, , double, decimal, , nint, , nuintenumerações.
Um relational_pattern é aplicável a um tipo T se um operador relacional binário interno adequado for definido com ambos os operandos do tipo Tou se houver uma conversão nula ou de unboxing explícita do T tipo da expressão constante.
É um erro de tempo de compilação se a expressão for avaliada como double.NaN, float.NaNou uma constante nula.
Quando o valor de entrada tem um tipo para o qual um operador relacional binário interno adequado é definido, a avaliação desse operador é tomada como o significado do padrão relacional. Caso contrário, o valor de entrada será convertido no tipo da expressão constante usando uma conversão nula ou de unboxing explícita. Será um erro de tempo de compilação se não houver essa conversão. O padrão é considerado como não corresponder se a conversão falhar. Se a conversão for bem-sucedida, o resultado da operação de correspondência de padrões será o resultado da avaliação da expressão e «op» v em que e está a entrada convertida, «op» é o operador relacional e v é a expressão constante.
Exemplo:
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", };A saída produzida é
Too high Unknown Acceptableexemplo de fim
11.2.10 Padrão lógico
Um logical_pattern é usado para negar o resultado de uma correspondência de padrão ou para combinar os resultados de várias correspondências de padrão usando conjunção (and) ou disjunção (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, ande or são coletivamente chamados de operadores padrão.
Um negated_pattern corresponderá se o padrão que está sendo negado não corresponder e vice-versa. Um conjunctive_pattern requer que ambos os padrões correspondam. Um disjunctive_pattern requer que qualquer padrão corresponda. Ao contrário de seus equivalentes de operador de && idioma e or||and operadores de curto-circuito e não são operadores de curto-circuito.
É um erro de tempo de compilação para que uma variável padrão seja declarada abaixo de um not operador ou or padrão.
Observação: como nem
notorpode produzir uma atribuição definitiva para uma variável de padrão, é um erro declarar uma nessas posições. nota final
Em um conjunctive_pattern, o tipo de entrada do segundo padrão é restringido pelos requisitos de restrição de tipo do primeiro padrão do and. O tipo estreito de um padrão P é definido da seguinte maneira:
- Se
Pfor um padrão de tipo, o tipo estreito será o tipo do tipo do padrão de tipo. - Caso contrário, se
Pfor um padrão de declaração, o tipo restrito será o tipo do tipo do padrão de declaração. - Caso contrário, se
Pfor um padrão recursivo que fornece um tipo explícito, o tipo restrito será esse tipo. - Caso contrário, se
Pfor correspondido por meio das regras deITupleum positional_pattern (§11.2.5), o tipo restrito será o tipoSystem.ITuple. - Caso contrário, se
Pfor um padrão constante em que a constante não é a constante nula e onde a expressão não tem conversão de expressão constante para o tipo de entrada, o tipo estreito é o tipo da constante. - Caso contrário, se
Pfor um padrão relacional em que a expressão constante não tenha conversão de expressão constante para o tipo de entrada, o tipo estreito será o tipo da constante. - Caso contrário, se
Pfor um padrãoor, o tipo estreito será o tipo comum do tipo estreito dos subpadrões se esse tipo comum existir. Para essa finalidade, o algoritmo de tipo comum considera apenas conversões de identidade, boxe e referência implícita e considera todos os subpadrões de uma sequência deorpadrões (ignorando padrões parênteses). - Caso contrário, se
Pfor um padrãoand, o tipo estreito será o tipo estreito do padrão certo. Além disso, o tipo estreito do padrão esquerdo é o tipo de entrada do padrão direito. - Caso contrário, o tipo restrito é
Po tipo dePentrada.
Observação: conforme indicado pela gramática,
nottem precedência sobreand, que tem precedência sobreor. Isso pode ser explicitamente indicado ou substituído usando parênteses. nota final
Quando um padrão aparece no lado direito de is, a extensão do padrão é determinada pela gramática; como resultado, os operadores andpadrão , ore not dentro do padrão se associam mais fortemente do que os operadores lógicos &&, ||e ! fora do padrão.
Exemplo:
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", };A saída produzida é
High Too low Acceptableexemplo de fim
Exemplo:
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}."), };A saída produzida é
winter autumn springexemplo de fim
Exemplo:
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}");A saída produzida é
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: Trueexemplo de fim
11.3 Subsunção de padrão
Em uma instrução switch, é um erro se o padrão de um caso for incluído pelo conjunto anterior de casos não protegidos (§13.8.3). Informalmente, isso significa que qualquer valor de entrada teria sido correspondido por um dos casos anteriores. As regras a seguir definem quando um conjunto de padrões inclui um determinado padrão:
Um padrão Pcorresponderia a uma constante K se qualquer uma das seguintes condições se mantiver:
- a especificação para o comportamento de runtime desse padrão é que
PcorrespondeK. -
Pé um type_pattern para o tipoTeKnãonullé e o tipo deKruntime éTou um tipo derivado ouTum tipo que implementaT. -
Pé um relational_pattern com o operador «op» e constantev, e a expressãoK«op»vseria avaliada comotrue. -
Pé um negated_patternnot P₁eP₁não corresponderiaK. -
Pé um conjunctive_patternP₁ and P₂e ambosP₁corresponderiamKeP₂corresponderiamK. -
Pé um disjunctive_patternP₁ or P₂eP₁corresponderiaKouP₂corresponderiaK. -
Pé um discard_pattern.
Um conjunto de padrões Qinclui um padrão P se qualquer uma das seguintes condições for válida:
-
Pé um padrão constante e qualquer um dos padrões no conjuntoQcorresponderiaPao valor convertido -
Pé um padrão var e o conjunto de padrõesQé exaustivo (§11.4) para o tipo do valor de entrada do padrão (§11.1), e o valor de entrada do padrão não é de um tipo anulável ou algum padrão emQcorresponderia anull. -
Pé um padrão de declaração com tipoTe o conjunto de padrõesQé exaustivo para o tipoT(§11.4). -
Pé um type_pattern para o tipoTe o conjunto de padrõesQé exaustivo para o tipoT. -
Pé um relational_pattern com o operador «op» e o valorvconstante e, para cada valor do tipo de entrada que satisfaz a relação «op»v, algum padrão no conjuntoQcorresponderia a esse valor. -
Pé um disjunctive_patternP₁ or P₂e o conjunto de subsumesP₁eQsubsumesP₂de padrõesQ. -
Pé um conjunctive_patternP₁ and P₂e pelo menos um dos seguintes retenções:QsubsumesP₁ouQsubsumesP₂. -
Pé um negated_patternnot P₁eQé exaustivo para o tipo de entrada considerando apenas os valores não correspondidos porP₁. -
Pé um discard_pattern e o conjunto de padrõesQé exaustivo para o tipo do valor de entrada padrão e o valor de entrada padrão não é de um tipo anulável ou algum padrão emQcorresponderianull. - Algum padrão é
Qum disjunctive_patternQ₁ or Q₂e substituir esse padrãoQ₁pelo inQproduziria um conjunto que subsumePou substituí-loQ₂produziria um conjunto que subsumeP. - Algum padrão é
Qum negated_patternnot Q₁ePnão corresponderia a nenhum valor queQ₁correspondesse.
11.4 Exaustividade do padrão
Informalmente, um conjunto de padrões é exaustivo para um tipo se, para cada valor possível desse tipo diferente de nulo, algum padrão no conjunto for aplicável. As regras a seguir definem quando um conjunto de padrões é exaustivo para um tipo:
Um conjunto de padrões Q é exaustivo para um tipo T se qualquer uma das seguintes condições for válida:
-
Té um tipo integral ou enumeração, ou uma versão anulável de um desses, e para cada valor possível doTtipo subjacente não anulável de , algum padrão emQcorresponderia a esse valor; ou - Algum padrão em
Qé um padrão var; ou - Algum padrão é
Qum padrão de declaração para o tipoD, e há uma conversão de identidade, uma conversão de referência implícita ou uma conversão de boxing deTparaD; ou - Algum padrão é
Qum type_pattern para o tipoD, e há uma conversão de identidade, uma conversão de referência implícita ou uma conversão de boxing deTparaD; ou - Algum padrão é
Qum discard_pattern; ou - Os padrões incluem
Quma combinação de relational_patterne constant_patterncujos intervalos abrangem coletivamente todos os valores possíveis doTtipo subjacente não anulável. Parafloatedoubletipos, isso incluiSystem.Double.NaNouSystem.Single.NaN, respectivamente, uma vezNaNque não é correspondido por nenhum padrão relacional; ou - Algum padrão é
Qum disjunctive_patternP₁ or P₂e substituir esse padrão por ambosP₁eP₂emQgera um conjunto que é exaustivo paraT; ou - Algum padrão é
Qum negated_patternnot P₁e os padrões emQconjunto com os valores não correspondidosP₁abrangem todos os valores possíveis deT. Um negated_patternnot P₁é exaustivo por si só quandoP₁não corresponde a nenhum valor possível deT; ou - Algum padrão é
Qum conjunctive_patternP₁ and P₂, e o conjunto que contém apenasP₁é exaustivo eTo conjunto que contém apenasP₂é exaustivo paraT.
Observação: quando um padrão de tipo inclui tipos anuláveis, o padrão pode ser exaustivo para o tipo, mas ainda gerar um aviso porque o padrão de tipo não corresponderá a um
nullvalor. nota final
Observação: para tipos de ponto flutuante, a combinação de padrões
< 0e>= 0não é exaustiva porque nenhum padrão relacional correspondeNaN. Um conjunto exaustivo correto seria< 0,>= 0edouble.NaN(oufloat.NaN). nota final
Exemplo:
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; } }exemplo de fim
ECMA C# draft specification