11 Padrões e correspondência de padrões
11.1 Geral
Um padrão é uma forma sintática que pode ser usada com o is
operador (§12.12.12) e em um switch_statement (§13.8.3) para expressar a forma dos dados com os quais os dados de entrada devem ser comparados. Um padrão é testado em relação à expressão de uma instrução switch ou em relação a um relational_expression que está no lado esquerdo de um is
operador, cada um dos quais é chamado de valor de entrada de 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
;
Uma declaration_pattern e uma var_pattern podem 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
v
compilação de éTextReader
. Uma variável do tipoTextReader
nunca 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
v
compilação de éobject
. Uma variável do tipoobject
pode 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.
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 o valor em uma variável desse tipo.
declaration_pattern
: type simple_designation
;
simple_designation
: single_variable_designation
;
single_variable_designation
: identifier
;
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.12.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). Esse formulário de padrão nunca corresponde a um null
valor.
Observação: a expressão
e is T
is-type e o padrãoe is T _
de declaração são equivalentes quandoT
não são um tipo anulável. nota final
Dado um valor de entrada padrão (§11.1) e, se o simple_designation for o identificador _
, ele denota um descarte (§9.2.9.1) 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 nesse contexto.) Se simple_designation for qualquer outro identificador, uma variável local (§9.2.9) do tipo fornecido nomeado pelo identificador fornecido 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 T
E
for um tipo aberto (§8.4.3). Um padrão de declaração que nomeia um tipo T
é aplicável a todos os tipos E
para os quais E
o padrão é compatível com T
.
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
É um erro se type for um tipo de valor anulável.
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 id
de tipo se o valor não for nulo eT2
forT
, 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
if
instrução estátrue
em tempo de execução e a variávelv
contém o valor3
do tipoint
dentro do bloco. 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 P
constante , 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
P
corresponderá ao valor E se o resultado da expressãoe == v
fortrue
; caso contrário - O padrão
P
corresponde ao valor e seobject.Equals(e, v)
retornatrue
.
Exemplo: a
switch
instruçã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_pattern corresponde 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
;
Dado um valor de entrada de padrão (§11.1) e, se a designação for o identificador_
, ele denota um descarte (§9.2.9.1) 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 nesse contexto.) Se a designação for qualquer outro identificador, no tempo de execução, o valor de e será 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 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.
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 P
corresponderia 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 Q
inclui 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 conjuntoQ
corresponderiaP
ao valor convertido deP
é 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 emQ
corresponderia anull
.P
é um padrão de declaração com tipoT
e 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 doT
tipo subjacente não anulável de , algum padrão emQ
corresponderia 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 deT
paraD
.
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