Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
11.1 Generalidades
Um padrão é uma forma sintática que pode ser usada com o is operador (§12.15.12), numa switch_statement (§13.8.3) e numa switch_expression (§12.12) para expressar a forma dos dados contra os quais os dados recebidos devem ser comparados. Os padrões podem ser recursivos, de modo que partes dos dados podem ser comparadas com subpadrõ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 maiúsculas e minúsculas é 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 switch.
- Em contextos aninhados, o subpadrão é testado em relação a valores recuperados de propriedades, campos ou indexados de outros valores de entrada, dependendo do formulário de padrão.
O valor em relação ao qual um padrão é testado é chamado de valor de entrada do padrão.
11.2 Formas padrão
11.2.1 Generalidades
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 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 de 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 aparece 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 em tempo de compilação porque o tipo de tempo de compilação de
véTextReader. Uma variável do tipoTextReadernunca pode ter um valor que seja compatível com a 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 em tempo de compilação porque o tipo em tempo de compilação é
vobject. Uma variável do tipoobjectpode ter um valor compatível com a referência comstring:object v = Console.In; if (v is string s) { // code assuming v is a string }Exemplo final
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 das operações e efeitos colaterais durante a correspondência de padrões (chamadas para , acessos à Deconstructpropriedade e invocações de métodos em System.ITuple) não é especificada.
11.2.2 Modelo da declaração
Um declaration_pattern é usado para testar se um valor tem um determinado tipo e, se o teste for bem-sucedido, para opcionalmente fornecer 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 _ será considerado um discard_designation e não um single_variable_designation.
O tipo de execução do valor é testado contra o tipo no padrão 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. É um erro em 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). Este formulário de padrão nunca corresponde a um null valor.
Nota: A expressão
e is Tdo tipo is 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 denota um descarte (§9.2.9.2), e o valor de e não está vinculado 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, é introduzida uma variável local (§9.2.9) do tipo dado nomeado pelo identificador dado. A essa variável local é atribuído o valor do valor de entrada do padrão quando o padrão corresponde ao valor.
Certas combinações do tipo estático do valor de entrada do padrão e do tipo dado são consideradas incompatíveis e resultam em um erro em tempo de compilação. Diz-se que um valor de tipo E estático é compatível com o tipoT se existir uma conversão de identidade, uma conversão de referência implícita ou explícita, uma conversão de boxing, uma conversão de unboxing, ou uma conversão implícita ou explícita de tipo de valor nulo de E para T, ou se qualquer um E de ou T for um tipo aberto (§8.4.3). Um padrão de declaração nomeando um tipo T é aplicável a.
Nota: O suporte para tipos abertos pode ser mais útil ao verificar tipos que podem ser struct ou tipos de classe, e o boxe deve ser evitado. Nota final
Exemplo: O padrão de declaração é útil para executar testes de tipo em 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 final
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 in a box)Tcorresponde a um padrãoT2 idde tipo se o valor for não-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ávelvmantém o valor3do tipointdentro do bloco. Após o bloco, a variávelvestá no escopo, mas não definitivamente atribuída. Exemplo final
11.2.3 Padrão constante
Um constant_pattern é usado para testar o valor de um valor de entrada padrão (§11.1) em relação ao valor constante dado.
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 enum, o valor constante do padrão convertido para esse 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 um tipo de enum, o valor constante do padrão convertido para seu tipo subjacente; caso contrário,
- O valor do valor constante do padrão.
Dado um valor de entrada padrão e e um padrão P constante com valor convertido v,
- se e tem tipo integral ou tipo enum, ou uma forma anulável de um destes, e v tem tipo integral, o padrão
Pcorresponde ao valor e se o resultado da expressãoe == vfortrue; - 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 maiúsculas e minúsculas.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 final
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, denota uma eliminação (§9.2.9.2), e o valor de e não está vinculado 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, no tempo de execução o valor de e é vinculado 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 do padrão é atribuído a essa variável local.
É um erro se o nome var se ligar a um tipo onde um var_pattern é usado.
Se a designação for um tuple_designation, o padrão é equivalente a um positional_pattern (§11.2.5) da (var do formulário, ...
) quando as denominaçõess são as que se encontram no tuple_designation. Por exemplo, o padrão var (x, (y, z)) é equivalente a (var x, (var y, var z)).
11.2.5 Padrão de posição
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ão adicional nos valores resultantes. Ele também suporta uma sintaxe de padrão semelhante a uma tupla (sem que o tipo seja 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 tempo de execução da expressão implementa System.ITuple.
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern (',' subpattern)*
;
subpattern
: pattern
| identifier ':' pattern
;
Dada uma correspondência de um valor de entrada com ossubpadrões( do tipo) padrão, um método é selecionado pesquisando em tipo por declarações acessíveis de e selecionando uma entre elas usando as mesmas regras que para a declaração de Deconstruct desconstrução.
É um erro se um positional_pattern omite o tipo, tem um único subpadrão sem um identificador, não tem property_subpattern e não tem simple_designation. Isso desambigua entre um constant_pattern que está entre parênteses e um positional_pattern.
A fim de extrair os valores para corresponder aos padrões na lista,
- Se o tipo for omitido e o tipo da expressão de entrada for um tipo de tupla, então o número de subpadrões deve ser o mesmo que a cardinalidade da tupla. Cada elemento de tupla é combinado com o subpadrão correspondente, e a correspondência é bem-sucedida se todos eles forem bem-sucedidos. Se qualquer subpadrão tiver um identificador, este deve nomear um elemento de tupla na posição correspondente no tipo de tupla.
- Caso contrário, se existir um adequado
Deconstructcomo um membro do tipo, é um erro em tempo de compilação se o tipo do valor de entrada não for compatível com o padrão. No tempo de execução, o valor de entrada é testado em relação ao tipo. Se isso falhar, a correspondência do padrão posicional falhará. Se for bem-sucedido, o valor de entrada será convertido para esse tipo eDeconstructinvocado com novas 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 será bem-sucedida se todos eles forem bem-sucedidos. Se qualquer subpadrão tiver um identificador, esse deve nomear um parâmetro na posição correspondente deDeconstruct. - Caso contrário, se o tipo for omitido, e o valor de entrada for do tipo
objectou de algum tipo que possa ser convertido porSystem.ITupleuma 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 em tempo de compilação.
A ordem na qual os subpadrões são correspondidos no tempo de execução não é especificada, e uma correspondência com falha pode não tentar corresponder a todos os subpadrões.
Exemplo: Aqui, desconstruímos um resultado de expressão e fazemos a correspondência entre os valores resultantes e 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 final
Exemplo: Os nomes dos elementos da tupla e dos parâmetros Deconstruct podem ser usados em um padrão posicional, da seguinte forma:
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 produção produzida é
Sum of [10 20 30] is 60Exemplo final
11.2.6 Padrão de propriedade
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.
É um erro em 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).
Nota: Um padrão de verificação nula cai fora de um padrão de propriedade trivial. Para verificar se a cadeia de caracteres
snão é nula, pode-se escrever 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{do tipo} de padrão, é um erro em tempo de compilação se a expressão e não for compatível com o tipo T designado pelo tipo. Se o tipo estiver ausente, o tipo é assumido como sendo o tipo estático de e. Cada um dos identificadores que aparecem no lado esquerdo dos seus subpadrões deve designar uma propriedade acessível e legível ou campo de T. Se a simple_designation do property_pattern estiver presente, declara uma variável padrão do tipo T.
No tempo de execução, 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 campo ou propriedade property_subpattern será lido e seu valor correspondido ao padrão correspondente. O resultado de toda a partida é false apenas se o resultado de qualquer um destes 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 tempo de execução. Se a correspondência for bem-sucedida e a simple_designation do property_pattern for um single_variable_designation, a variável declarada receberá o 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 final
Exemplo: Uma verificação de tipo em tempo de execução e uma declaração de variável podem ser adicionadas a um padrão de propriedade, da seguinte forma:
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 produção produzida é
Hello Hi! 12345 abcExemplo final
11.2.7 Padrão de eliminação
Cada expressão corresponde ao padrão de descarte, o que resulta no valor da expressão que está sendo descartada.
discard_pattern
: '_'
;
É um erro em tempo de compilação usar um padrão de descarte em uma relational_expression dopadrão de is de forma ou como o padrão de um switch_label.
Nota: 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 produção produzida é
5.0 0.0 0.0Aqui, é usado um padrão de descarte para tratar
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 final
11.2.8 Padrão de tipo
Um type_pattern é usado para testar se o valor de entrada do padrão (§11.1) tem um dado 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 seja compatível com T (§11.2.2).
O tipo de execução do valor é testado contra o tipo usando as mesmas regras especificadas no operador is-type (§12.15.12.1). Se o teste for bem-sucedido, o padrão corresponde a esse valor. É um erro em tempo de compilação se o tipo for um tipo anulável. Este 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 do padrão (§11.1) contra um valor constante.
relational_pattern
: '<' relational_expression
| '<=' relational_expression
| '>' relational_expression
| '>=' relational_expression
;
A relational_expression numa relational_pattern é necessária para avaliar a um valor constante.
Os padrões relacionais suportam os operadores <relacionais , <=, >, e >= em todos os tipos incorporados que suportam tais operadores relacionais binários, com ambos os operandos a terem o mesmo tipo: sbyte, byte, short, ushort, charuintnintintulongnuintlongfloatdoubledecimale enums.
Uma relational_patterné aplicável a um tipo T se um operador relacional binário incorporado adequado for definido com ambos os operandos do tipo T, ou se existir uma conversão explícita de nullable ou unboxing de T para o tipo da expressão constante.
É um erro em tempo de compilação se a expressão for avaliada para double.NaN, float.NaN, ou uma constante nula.
Quando o valor de entrada tem um tipo para o qual está definido um operador relacional binário incorporado adequado, a avaliação desse operador é tomada como o significado do padrão relacional. Caso contrário, o valor de entrada é convertido para o tipo da expressão constante usando uma conversão explícita anulável ou unboxing. É um erro em tempo de compilação se não existir tal conversão. Considera-se que o padrão não corresponde se a conversão falhar. Se a conversão for bem-sucedida, o resultado da operação de correspondência de padrões é o resultado da avaliação da expressão e «op» v onde e é 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 produção produzida é
Too high Unknown AcceptableExemplo final
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 múltiplas correspondências de padrões 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, and, e or são coletivamente chamados operadores de padrão.
Um negated_pattern corresponde se o padrão que está a ser negado não coincidir, e vice-versa. Um conjunctive_pattern requer que ambos os padrões coincidam. Um disjunctive_pattern requer que qualquer um dos padrões coincida. Ao contrário dos seus homólogos operadores de linguagem, && e ||, and e ornão são operadores de curto-circuito.
É um erro em tempo de compilação para uma variável de padrão ser declarada abaixo de um not operador de padrão.or
Nota: Como nem
notornem podem produzir uma atribuição definida para uma variável padrão, é erro declarar uma nessas posições. Nota final
Numa conjunctive_pattern, o tipo de entrada do segundo padrão é restringido pelos requisitos de estreitamento do primeiro padrão do and. O tipo estreitado de um padrão P é definido da seguinte forma:
- Se
Pfor um padrão tipo, o tipo estreitado é o tipo do tipo do padrão tipo. - Caso contrário, se
Pfor um padrão de declaração, o tipo restringido é o tipo do tipo do padrão de declaração. - Caso contrário, se
Pfor um padrão recursivo que dá um tipo explícito, o tipo restringido é esse tipo. - Caso contrário, se
Pfor emparelhado através das regras paraITuplenum positional_pattern (§11.2.5), o tipo restringido é o tipoSystem.ITuple. - Caso contrário, se
Pfor um padrão constante onde a constante não é a constante nula e onde a expressão não tem conversão constante para o tipo de entrada, o tipo estreitado é o tipo da constante. - Caso contrário, se
Pfor um padrão relacional onde a expressão constante não tem conversão de expressão constante para o tipo de entrada, o tipo estreitado é o tipo da constante. - Caso contrário, se
Pfor umorpadrão, o tipo estreitado é o tipo comum do tipo estreitado dos subpadrões, se tal tipo comum existir. Para este fim, o algoritmo de tipos comuns considera apenas identidade, boxing e conversões de referência implícitas, e considera todos os subpadrões de uma sequência deorpadrões (ignorando padrões entre parênteses). - Caso contrário, se
Pfor umandpadrão, o tipo estreitado é o tipo estreitado do padrão direito. Além disso, o tipo estreitado do padrão à esquerda é o tipo de entrada do padrão à direita. - Caso contrário, o tipo restringido de
PéPo tipo de entrada de .
Nota: Como indicado pela gramática,
nottem precedência sobreand, que tem precedência sobreor. Isto pode ser explicitamente indicado ou anulado 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 anddo padrão , or, e not dentro do padrão ligam-se mais apertadamente 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 produção produzida é
High Too low AcceptableExemplo final
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 produção produzida é
winter autumn springExemplo final
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 produção 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 final
11.3 Subsunção do padrão
Em uma instrução switch, é um erro se o padrão de um caso é subsumido 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 subsume um determinado padrão:
Um padrão Pcorresponderia a uma constante K se qualquer uma das seguintes condições se cumprir:
- A especificação para o comportamento em tempo de execução desse padrão é que
PcorrespondeKa . -
Pé um type_pattern para o tipoTeKnãonullé e o tipo de tempo de execução éKTou um tipo derivado deTou um tipo que implementaT. -
Pé uma relational_pattern com operador «op» e constantev, e a expressãoK«op»vavaliaria atrue. -
Pé um negated_patternnot P₁eP₁não corresponderiaK. -
Pé um conjunctive_patternP₁ and P₂e ambosP₁corresponderiamKeP₂seriam correspondentesK. -
Pé um disjunctive_patternP₁ or P₂e ouP₁corresponderiaKouP₂seria compatívelKcom . -
Pé um discard_pattern.
Um conjunto de padrões Qsubsume um padrão P se qualquer uma das seguintes condições se mantiver:
-
Pé um padrão constante e qualquer um dos padrões do conjuntoQcorresponderiaPao valor convertido de -
Pé um padrão var e o conjunto de padrõesQé exaustivo -
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é uma relational_pattern com operador «op» e 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 padrõesQsubsumeP₁eQsubsumeP₂. -
Pé um conjunctive_patternP₁ and P₂e pelo menos um dos seguintes é válido:QsubsumeP₁, ouQsubsumeP₂. -
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 do padrão, e ou o valor de entrada do padrão não é do tipo anulável ou algum padrão emQcorresponderianulla . - Algum padrão em
Qé um disjunctive_patternQ₁ or Q₂e substituir esse padrão porQ₁emQdaria um conjunto que subsumeP, ou substituí-lo porQ₂daria um conjunto que subsumeP. - Algum padrão em
Qé um 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 se mantiver:
-
Té um tipo integral ou enum, ou uma versão anulável de um desses, e para cada valor possível deT's tipo subjacente não anulável, algum padrão emQcorresponderia a esse valor; - Algum padrão em
Qé um padrão var; - Algum padrão em
Qé um padrão de declaração para o tipoD, e existe uma conversão de identidade, uma conversão implícita de referência, ou uma conversão boxing deTparaD; ou - Algum padrão em
Qé um type_pattern para o tipoD, e existe uma conversão de identidade, uma conversão implícita de referência, ou uma conversão boxing deTparaD; ou - Algum padrão em
Qé um discard_pattern; ou - Os padrões em
Qincluem uma combinação de relational_patterns e constant_patterns cujos intervalos cobrem coletivamente todos os valores possíveis doTtipo subjacente não nulável de . Parafloatedoubletipos, isto incluiSystem.Double.NaNouSystem.Single.NaNrespetivamente, já queNaNnão é correspondido por qualquer padrão relacional; ou - Algum padrão em
Qé um disjunctive_patternP₁ or P₂, e substituir esse padrão por ambosP₁eP₂emQproduz um conjunto exaustivo paraT; ou - Algum padrão em
Qé um negated_patternnot P₁, e os padrões emQjuntamente com os valores não correspondidos porP₁cobrem 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 em
Qé um conjunctive_patternP₁ and P₂, e o conjunto que contém apenasP₁é exaustivo paraTe o conjunto que contém apenasP₂é exaustivo paraT.
Nota: Quando um padrão tipo inclui tipos anuláveis, o padrão pode ser exaustivo para o tipo mas ainda assim gerar um aviso porque o padrão tipo não corresponde a um
nullvalor. Nota final
Nota: Para tipos de ponto flutuante, a combinação de padrões
< 0e>= 0não é exaustiva porque nenhum dos padrões relacionais correspondeNaNa . Um conjunto exaustivo correto seria< 0, , edouble.NaN(oufloat.NaN>= 0). 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 final
ECMA C# draft specification