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 é um formulário sintactico que pode ser usado 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 de 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
: 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 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 de é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 is-type (§12.14.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 sã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 tipo T se existir uma conversão de identidade, uma conversão de referência implícita ou explícita, uma conversão de boxing ou uma conversão de unboxing de E para T, ou se ou 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 designação do formulário(var, ...
) 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 o tipo
{de padrãoproperty_pattern_list}, 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 sua property_pattern_list 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ãode relational_expressionis 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
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 a especificação para o comportamento de tempo de execução desse padrão fosse que P correspondesse a K.
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 de -
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).
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 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 boxing 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 de fim
ECMA C# draft specification