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.14.12), em um switch_statement (§13.8.3) e em um switch_expression (§12.11) para expressar a forma dos dados com 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
: declaration_pattern
| constant_pattern
| var_pattern
| positional_pattern
| property_pattern
| discard_pattern
;
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 de 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 tempo de execução do valor é testado em relação ao tipo no padrão usando as mesmas regras especificadas no operador is-type (§12.14.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 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 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 tipo T se existir uma conversão de identidade, uma conversão de referência implícita ou explícita, uma conversão de boxe ou uma conversão de unboxing de E para T, ou se um ou ET 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 designação do formulário(var, ...
) 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 o tipo
{de padrão property_pattern_list}, é um erro em tempo de compilação se a expressão e não for compatível com o tipo T designado por tipo. Se o tipo estiver ausente, o tipo é assumido como sendo o tipo estático de e. Cada um dos identificadores que figuram no lado esquerdo do seu property_pattern_list designa uma propriedade ou campo de T acessível e legível. Se o simple_designation do property_pattern estiver presente, ele 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 relational_expressionis 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, 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 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 P corresponderia
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 no conjuntoQcorresponderiaPao valor convertido do -
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).
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; - Alguns padrões em
Qé um 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 boxe deTparaD.
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