Correspondência de padrões para C# 7Pattern Matching for C# 7

As extensões de correspondência de padrões para C# habilitam muitos dos benefícios dos tipos de dados algébricas e a correspondência de padrões de linguagens funcionais, mas de uma forma que se integre perfeitamente com a sensação da linguagem subjacente.Pattern matching extensions for C# enable many of the benefits of algebraic data types and pattern matching from functional languages, but in a way that smoothly integrates with the feel of the underlying language. Os recursos básicos são: tipos de registro, que são tipos cujo significado semântico é descrito pela forma dos dados; e correspondência de padrões, que é uma nova forma de expressão que permite a decomposição de vários níveis extremamente conciso desses tipos de dados.The basic features are: record types, which are types whose semantic meaning is described by the shape of the data; and pattern matching, which is a new expression form that enables extremely concise multilevel decomposition of these data types. Os elementos dessa abordagem são inspirados por recursos relacionados nas linguagens de programação F # e escala.Elements of this approach are inspired by related features in the programming languages F# and Scala.

Expressão IsIs expression

O is operador é estendido para testar uma expressão em relação a um padrão.The is operator is extended to test an expression against a pattern.

relational_expression
    : relational_expression 'is' pattern
    ;

Essa forma de relational_expression é além dos formulários existentes na especificação do C#.This form of relational_expression is in addition to the existing forms in the C# specification. Será um erro de tempo de compilação se o relational_expression à esquerda do token não is designar um valor ou não tiver um tipo.It is a compile-time error if the relational_expression to the left of the is token does not designate a value or does not have a type.

Cada identificador do padrão apresenta uma nova variável local que é definitivamente atribuída após o is operador true (ou seja, definitivamente atribuído quando true).Every identifier of the pattern introduces a new local variable that is definitely assigned after the is operator is true (i.e. definitely assigned when true).

Observação: há tecnicamente uma ambiguidade entre o tipo em um is-expression e constant_pattern, que pode ser uma análise válida de um identificador qualificado.Note: There is technically an ambiguity between type in an is-expression and constant_pattern, either of which might be a valid parse of a qualified identifier. Tentamos associá-lo como um tipo de compatibilidade com as versões anteriores do idioma; somente se isso falhar, resolveremos como fazemos em outros contextos, para a primeira coisa encontrada (que deve ser uma constante ou um tipo).We try to bind it as a type for compatibility with previous versions of the language; only if that fails do we resolve it as we do in other contexts, to the first thing found (which must be either a constant or a type). Essa ambiguidade só está presente no lado direito de uma is expressão.This ambiguity is only present on the right-hand-side of an is expression.

PadrõesPatterns

Padrões são usados no is operador e em uma switch_statement para expressar a forma de dados em que os dados de entrada devem ser comparados.Patterns are used in the is operator and in a switch_statement to express the shape of data against which incoming data is to be compared. Os padrões podem ser recursivos para que as partes dos dados possam ser correspondidas em subpadrões.Patterns may be recursive so that parts of the data may be matched against sub-patterns.

pattern
    : declaration_pattern
    | constant_pattern
    | var_pattern
    ;

declaration_pattern
    : type simple_designation
    ;

constant_pattern
    : shift_expression
    ;

var_pattern
    : 'var' simple_designation
    ;

Observação: há tecnicamente uma ambiguidade entre o tipo em um is-expression e constant_pattern, que pode ser uma análise válida de um identificador qualificado.Note: There is technically an ambiguity between type in an is-expression and constant_pattern, either of which might be a valid parse of a qualified identifier. Tentamos associá-lo como um tipo de compatibilidade com as versões anteriores do idioma; somente se isso falhar, resolveremos como fazemos em outros contextos, para a primeira coisa encontrada (que deve ser uma constante ou um tipo).We try to bind it as a type for compatibility with previous versions of the language; only if that fails do we resolve it as we do in other contexts, to the first thing found (which must be either a constant or a type). Essa ambiguidade só está presente no lado direito de uma is expressão.This ambiguity is only present on the right-hand-side of an is expression.

Padrão de declaraçãoDeclaration pattern

O declaration_pattern testa se uma expressão é de um determinado tipo e a converte para esse tipo se o teste for bem sucedido.The declaration_pattern both tests that an expression is of a given type and casts it to that type if the test succeeds. Se o simple_designation for um identificador, ele apresentará uma variável local do tipo fornecido nomeado pelo identificador fornecido.If the simple_designation is an identifier, it introduces a local variable of the given type named by the given identifier. Essa variável local é definitivamente atribuída quando o resultado da operação de correspondência de padrões é true.That local variable is definitely assigned when the result of the pattern-matching operation is true.

declaration_pattern
    : type simple_designation
    ;

A semântica de tempo de execução dessa expressão é que ela testa o tipo de tempo de execução do operando de relational_expression à esquerda em relação ao tipo no padrão.The runtime semantic of this expression is that it tests the runtime type of the left-hand relational_expression operand against the type in the pattern. Se for desse tipo de tempo de execução (ou algum subtipo), o resultado de is operator é true .If it is of that runtime type (or some subtype), the result of the is operator is true. Ele declara uma nova variável local denominada pelo identificador que recebe o valor do operando à esquerda quando o resultado é true .It declares a new local variable named by the identifier that is assigned the value of the left-hand operand when the result is true.

Determinadas combinações de tipo estático do lado esquerdo e do tipo fornecido são consideradas incompatíveis e resultam em erro de tempo de compilação.Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. Um valor de tipo estático E é considerado como padrão compatível com o tipo T se houver uma conversão de identidade, uma conversão de referência implícita, uma conversão boxing, uma conversão de referência explícita ou uma conversão unboxing de E para T .A value of static type E is said to be pattern compatible with the type T if there exists an identity conversion, an implicit reference conversion, a boxing conversion, an explicit reference conversion, or an unboxing conversion from E to T. É um erro de tempo de compilação se uma expressão do tipo E não for compatível com o tipo de padrão de tipo no qual ele é correspondido.It is a compile-time error if an expression of type E is not pattern compatible with the type in a type pattern that it is matched with.

Observação: no C# 7,1, estendemos isso para permitir uma operação de correspondência de padrões se o tipo de entrada ou o tipo T for um tipo aberto.Note: In C# 7.1 we extend this to permit a pattern-matching operation if either the input type or the type T is an open type. Este parágrafo é substituído pelo seguinte:This paragraph is replaced by the following:

Determinadas combinações de tipo estático do lado esquerdo e do tipo fornecido são consideradas incompatíveis e resultam em erro de tempo de compilação.Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. Um valor de tipo estático E é considerado como padrão compatível com o tipo T se houver uma conversão de identidade, uma conversão de referência implícita, uma conversão boxing, uma conversão de referência explícita ou uma conversão unboxing de E para T ou se E T for ou for um tipo aberto.A value of static type E is said to be pattern compatible with the type T if there exists an identity conversion, an implicit reference conversion, a boxing conversion, an explicit reference conversion, or an unboxing conversion from E to T, or if either E or T is an open type. É um erro de tempo de compilação se uma expressão do tipo E não for compatível com o tipo de padrão de tipo no qual ele é correspondido.It is a compile-time error if an expression of type E is not pattern compatible with the type in a type pattern that it is matched with.

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 idiomaThe declaration pattern is useful for performing run-time type tests of reference types, and replaces the idiom

var v = expr as Type;
if (v != null) { // code using v }

Com um pouco mais concisoWith the slightly more concise

if (expr is Type v) { // code using v }

Erro se o tipo for um tipo de valor anulável.It is an error if type is a nullable value type.

O padrão de declaração pode ser usado para testar valores de tipos anuláveis: um valor do tipo Nullable<T> (ou um Boxed T ) corresponde a um padrão de tipo T2 id se o valor for não nulo e o tipo de T2 for ou T algum tipo ou interface base de T .The declaration pattern can be used to test values of nullable types: a value of type Nullable<T> (or a boxed T) matches a type pattern T2 id if the value is non-null and the type of T2 is T, or some base type or interface of T. Por exemplo, no fragmento de códigoFor example, in the code fragment

int? 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ável v contém o valor 3 do tipo int dentro do bloco.The condition of the if statement is true at runtime and the variable v holds the value 3 of type int inside the block.

Padrão de constanteConstant pattern

constant_pattern
    : shift_expression
    ;

Um padrão constante testa o valor de uma expressão em relação a um valor constante.A constant pattern tests the value of an expression against a constant value. A constante pode ser qualquer expressão constante, como um literal, o nome de uma variável declarada const ou uma constante de enumeração ou uma typeof expressão.The constant may be any constant expression, such as a literal, the name of a declared const variable, or an enumeration constant, or a typeof expression.

Se e e c forem de tipos integrais, o padrão será considerado correspondido se o resultado da expressão e == c for true .If both e and c are of integral types, the pattern is considered matched if the result of the expression e == c is true.

Caso contrário, o padrão será considerado correspondente se object.Equals(e, c) retornar true .Otherwise the pattern is considered matching if object.Equals(e, c) returns true. Nesse caso, é um erro de tempo de compilação se o tipo estático de e não for compatível com o tipo da constante.In this case it is a compile-time error if the static type of e is not pattern compatible with the type of the constant.

Padrão de varVar pattern

var_pattern
    : 'var' simple_designation
    ;

Uma expressão e corresponde a uma var_pattern sempre.An expression e matches a var_pattern always. Em outras palavras, uma correspondência a um padrão de var sempre é realizada com sucesso.In other words, a match to a var pattern always succeeds. Se o simple_designation for um identificador, em tempo de execução o valor de e será associado a uma variável local introduzida recentemente.If the simple_designation is an identifier, then at runtime the value of e is bound to a newly introduced local variable. O tipo da variável local é o tipo estático de e.The type of the local variable is the static type of e.

Erro se o nome for var associado a um tipo.It is an error if the name var binds to a type.

Instrução switchSwitch statement

A switch instrução é estendida para selecionar para execução o primeiro bloco com um padrão associado que corresponda à expressão de comutador.The switch statement is extended to select for execution the first block having an associated pattern that matches the switch expression.

switch_label
    : 'case' complex_pattern case_guard? ':'
    | 'case' constant_expression case_guard? ':'
    | 'default' ':'
    ;

case_guard
    : 'when' expression
    ;

A ordem na qual os padrões são correspondidos não está definida.The order in which patterns are matched is not defined. Um compilador tem permissão para corresponder padrões fora de ordem e reutilizar os resultados de padrões já correspondidos para calcular o resultado da correspondência de outros padrões.A compiler is permitted to match patterns out of order, and to reuse the results of already matched patterns to compute the result of matching of other patterns.

Se uma proteção de caso estiver presente, sua expressão será do tipo bool .If a case-guard is present, its expression is of type bool. Ele é avaliado como uma condição adicional que deve ser satisfeita para que o caso seja considerado satisfeito.It is evaluated as an additional condition that must be satisfied for the case to be considered satisfied.

Erro se um switch_label não puder ter nenhum efeito no tempo de execução porque seu padrão é incorporadas por casos anteriores.It is an error if a switch_label can have no effect at runtime because its pattern is subsumed by previous cases. [TODO: devemos ser mais precisos sobre as técnicas que o compilador precisa usar para alcançar esse julgamento.][TODO: We should be more precise about the techniques the compiler is required to use to reach this judgment.]

Uma variável de padrão declarada em um switch_label é definitivamente atribuída em seu bloco de caso se e somente se esse bloco de caso contiver precisamente um switch_label.A pattern variable declared in a switch_label is definitely assigned in its case block if and only if that case block contains precisely one switch_label.

[TODO: devemos especificar quando um bloco de alternância pode ser acessado.][TODO: We should specify when a switch block is reachable.]

Escopo de variáveis de padrãoScope of pattern variables

O escopo de uma variável declarada em um padrão é o seguinte:The scope of a variable declared in a pattern is as follows:

  • Se o padrão for um rótulo de caso, o escopo da variável será o bloco de caso.If the pattern is a case label, then the scope of the variable is the case block.

Caso contrário, a variável é declarada em uma expressão is_pattern , e seu escopo é baseado na construção que está delimitando imediatamente a expressão que contém a expressão is_pattern da seguinte maneira:Otherwise the variable is declared in an is_pattern expression, and its scope is based on the construct immediately enclosing the expression containing the is_pattern expression as follows:

  • Se a expressão estiver em uma lambda Expression-apto para, seu escopo será o corpo do lambda.If the expression is in an expression-bodied lambda, its scope is the body of the lambda.
  • Se a expressão estiver em um método ou Propriedade apto para de expressão, seu escopo será o corpo do método ou da propriedade.If the expression is in an expression-bodied method or property, its scope is the body of the method or property.
  • Se a expressão estiver em uma when cláusula de uma catch cláusula, seu escopo será essa catch cláusula.If the expression is in a when clause of a catch clause, its scope is that catch clause.
  • Se a expressão estiver em um iteration_statement, seu escopo será apenas essa instrução.If the expression is in an iteration_statement, its scope is just that statement.
  • Caso contrário, se a expressão estiver em algum outro formulário de instrução, seu escopo será o escopo que contém a instrução.Otherwise if the expression is in some other statement form, its scope is the scope containing the statement.

Com a finalidade de determinar o escopo, um embedded_statement é considerado em seu próprio escopo.For the purpose of determining the scope, an embedded_statement is considered to be in its own scope. Por exemplo, a gramática para uma if_statement éFor example, the grammar for an if_statement is

if_statement
    : 'if' '(' boolean_expression ')' embedded_statement
    | 'if' '(' boolean_expression ')' embedded_statement 'else' embedded_statement
    ;

Portanto, se a instrução controlada de um if_statement declarar uma variável de padrão, seu escopo será restrito a esse embedded_statement:So if the controlled statement of an if_statement declares a pattern variable, its scope is restricted to that embedded_statement:

if (x) M(y is var z);

Nesse caso, o escopo de z é a instrução incorporada M(y is var z); .In this case the scope of z is the embedded statement M(y is var z);.

Outros casos são erros por outros motivos (por exemplo, no valor padrão de um parâmetro ou em um atributo, ambos são um erro porque esses contextos exigem uma expressão constante).Other cases are errors for other reasons (e.g. in a parameter's default value or an attribute, both of which are an error because those contexts require a constant expression).

Em C# 7,3, adicionamos os seguintes contextos nos quais uma variável de padrão pode ser declarada:In C# 7.3 we added the following contexts in which a pattern variable may be declared:

  • Se a expressão estiver em um inicializador de Construtor, seu escopo será o inicializador de Construtor e o corpo do construtor.If the expression is in a constructor initializer, its scope is the constructor initializer and the constructor's body.
  • Se a expressão estiver em um inicializador de campo, seu escopo será o equals_value_clause em que ele aparece.If the expression is in a field initializer, its scope is the equals_value_clause in which it appears.
  • Se a expressão estiver em uma cláusula de consulta que é especificada para ser convertida no corpo de um lambda, seu escopo será apenas essa expressão.If the expression is in a query clause that is specified to be translated into the body of a lambda, its scope is just that expression.

Alterações na desambiguidade sintáticaChanges to syntactic disambiguation

Há situações que envolvem genéricos em que a gramática do C# é ambígua e a especificação da linguagem indica como resolver essas ambiguidades:There are situations involving generics where the C# grammar is ambiguous, and the language spec says how to resolve those ambiguities:

ambiguidades de gramática 7.6.5.27.6.5.2 Grammar ambiguities

As produções para nome simples (§ 7.6.3) e acesso de membro (§ 7.6.5) podem dar aumento às ambiguidades na gramática para expressões.The productions for simple-name (§7.6.3) and member-access (§7.6.5) can give rise to ambiguities in the grammar for expressions. Por exemplo, a instrução:For example, the statement:

F(G<A,B>(7));

pode ser interpretado como uma chamada para F com dois argumentos, G < A e B > (7) .could be interpreted as a call to F with two arguments, G < A and B > (7). Como alternativa, ele pode ser interpretado como uma chamada para F com um argumento, que é uma chamada para um método genérico G com dois argumentos de tipo e um argumento regular.Alternatively, it could be interpreted as a call to F with one argument, which is a call to a generic method G with two type arguments and one regular argument.

Se uma sequência de tokens puder ser analisada (no contexto) como um nome simples (§ 7.6.3), acesso de membro (§ 7.6.5) ou acesso de membro de ponteiro (§ 18.5.2) terminando com uma lista de argumentos de tipo (§ 4.4.1), o token imediatamente após o token de fechamento > será examinado.If a sequence of tokens can be parsed (in context) as a simple-name (§7.6.3), member-access (§7.6.5), or pointer-member-access (§18.5.2) ending with a type-argument-list (§4.4.1), the token immediately following the closing > token is examined. Se for um deIf it is one of

(  )  ]  }  :  ;  ,  .  ?  ==  !=  |  ^

em seguida, a lista de argumentos de tipo é mantida como parte do nome simples, acesso de membro ou ponteiro-acesso de membro e qualquer outra análise possível da sequência de tokens é descartada.then the type-argument-list is retained as part of the simple-name, member-access or pointer-member-access and any other possible parse of the sequence of tokens is discarded. Caso contrário, a lista de argumentos de tipo não é considerada como parte do nome simples, acesso de membro ou > ponteiro- acesso de membro, mesmo se não houver outra análise possível da sequência de tokens.Otherwise, the type-argument-list is not considered to be part of the simple-name, member-access or > pointer-member-access, even if there is no other possible parse of the sequence of tokens. Observe que essas regras não são aplicadas ao analisar uma lista de argumentos de tipo em um namespace-ou-Type-name (§ 3,8).Note that these rules are not applied when parsing a type-argument-list in a namespace-or-type-name (§3.8). A instruçãoThe statement

F(G<A,B>(7));

de acordo com essa regra, será interpretado como uma chamada para F com um argumento, que é uma chamada para um método genérico G com dois argumentos de tipo e um argumento regular.will, according to this rule, be interpreted as a call to F with one argument, which is a call to a generic method G with two type arguments and one regular argument. As instruçõesThe statements

F(G < A, B > 7);
F(G < A, B >> 7);

cada um será interpretado como uma chamada para F com dois argumentos.will each be interpreted as a call to F with two arguments. A instruçãoThe statement

x = F < A > +y;

será interpretado como um operador menor que, maior que operador, e operador de adição unário, como se a instrução tivesse sido gravada x = (F < A) > (+y) , em vez de como um nome simples com uma lista de argumentos de tipo seguido por um operador Binary Plus.will be interpreted as a less than operator, greater than operator, and unary plus operator, as if the statement had been written x = (F < A) > (+y), instead of as a simple-name with a type-argument-list followed by a binary plus operator. Na instruçãoIn the statement

x = y is C<T> + z;

os tokens C<T> são interpretados como um namespace-ou-tipo-nome com uma lista de argumentos de tipo.the tokens C<T> are interpreted as a namespace-or-type-name with a type-argument-list.

Há várias alterações introduzidas no C# 7 que tornam essas regras de Desambigüidade não mais suficientes para lidar com a complexidade da linguagem.There are a number of changes being introduced in C# 7 that make these disambiguation rules no longer sufficient to handle the complexity of the language.

Declarações de variável outOut variable declarations

Agora é possível declarar uma variável em um argumento out:It is now possible to declare a variable in an out argument:

M(out Type name);

No entanto, o tipo pode ser genérico:However, the type may be generic:

M(out A<B> name);

Como a gramática de idioma do argumento usa expression, esse contexto está sujeito à regra de desambiguidade.Since the language grammar for the argument uses expression, this context is subject to the disambiguation rule. Nesse caso, o fechamento > é seguido por um identificador, que não é um dos tokens que permite que ele seja tratado como uma lista de argumentos de tipo.In this case the closing > is followed by an identifier, which is not one of the tokens that permits it to be treated as a type-argument-list. Portanto, propondo para Adicionar o identificador ao conjunto de tokens que dispara a Desambigüidade para uma lista de argumentos de tipo.I therefore propose to add identifier to the set of tokens that triggers the disambiguation to a type-argument-list.

Declarações de tuplas e de desconstruçãoTuples and deconstruction declarations

Um literal de tupla é executado exatamente com o mesmo problema.A tuple literal runs into exactly the same issue. Considere a expressão de tuplaConsider the tuple expression

(A < B, C > D, E < F, G > H)

Nas regras antigas do C# 6 para analisar uma lista de argumentos, isso seria analisado como uma tupla com quatro elementos, começando com A < B o primeiro.Under the old C# 6 rules for parsing an argument list, this would parse as a tuple with four elements, starting with A < B as the first. No entanto, quando isso aparece à esquerda de uma desconstrução, queremos a desambiguidade disparada pelo token do identificador , conforme descrito acima:However, when this appears on the left of a deconstruction, we want the disambiguation triggered by the identifier token as described above:

(A<B,C> D, E<F,G> H) = e;

Essa é uma declaração de desconstrução que declara duas variáveis, a primeira que é do tipo A<B,C> e nomeada D .This is a deconstruction declaration which declares two variables, the first of which is of type A<B,C> and named D. Em outras palavras, o literal de tupla contém duas expressões, cada uma delas como uma expressão de declaração.In other words, the tuple literal contains two expressions, each of which is a declaration expression.

Para simplificar a especificação e o compilador, eu propondo que esse literal de tupla seja analisado como uma tupla de dois elementos onde quer que ele apareça (seja ou não exibido no lado esquerdo de uma atribuição).For simplicity of the specification and compiler, I propose that this tuple literal be parsed as a two-element tuple wherever it appears (whether or not it appears on the left-hand-side of an assignment). Isso seria um resultado natural da Desambigüidade descrita na seção anterior.That would be a natural result of the disambiguation described in the previous section.

Correspondência de padrõesPattern-matching

A correspondência de padrões introduz um novo contexto em que surge a ambiguidade do tipo de expressão.Pattern matching introduces a new context where the expression-type ambiguity arises. Anteriormente, o lado direito de um is operador era um tipo.Previously the right-hand-side of an is operator was a type. Agora ele pode ser um tipo ou uma expressão e, se for um tipo, pode ser seguido por um identificador.Now it can be a type or expression, and if it is a type it may be followed by an identifier. Isso pode, tecnicamente, alterar o significado do código existente:This can, technically, change the meaning of existing code:

var x = e is T < A > B;

Isso pode ser analisado sob as regras do C# 6 comoThis could be parsed under C#6 rules as

var x = ((e is T) < A) > B;

Mas, sob as regras do C# 7 (com a Inambiguidade proposta acima) seria analisado comobut under under C#7 rules (with the disambiguation proposed above) would be parsed as

var x = e is T<A> B;

que declara uma variável B do tipo T<A> .which declares a variable B of type T<A>. Felizmente, os compiladores nativo e Roslyn têm um bug no qual eles fornecem um erro de sintaxe no código C# 6.Fortunately, the native and Roslyn compilers have a bug whereby they give a syntax error on the C#6 code. Portanto, essa alteração significativa em particular não é uma preocupação.Therefore this particular breaking change is not a concern.

Correspondência de padrões introduz tokens adicionais que devem orientar a resolução de ambiguidade para selecionar um tipo.Pattern-matching introduces additional tokens that should drive the ambiguity resolution toward selecting a type. Os seguintes exemplos de código C# 6 válido serão desfeitos sem regras de Desambigüidade adicionais:The following examples of existing valid C#6 code would be broken without additional disambiguation rules:

var x = e is A<B> && f;            // &&
var x = e is A<B> || f;            // ||
var x = e is A<B> & f;             // &
var x = e is A<B>[];               // [

Alteração proposta na regra de desambiguidadeProposed change to the disambiguation rule

Eu propondo para revisar a especificação para alterar a lista de tokens de ambiguidade deI propose to revise the specification to change the list of disambiguating tokens from

(  )  ]  }  :  ;  ,  .  ?  ==  !=  |  ^

comoto

(  )  ]  }  :  ;  ,  .  ?  ==  !=  |  ^  &&  ||  &  [

E, em determinados contextos, tratamos o identificador como um token de ambiguidade.And, in certain contexts, we treat identifier as a disambiguating token. Esses contextos são onde a sequência de tokens que estão sendo desambiguados é imediatamente precedida por uma das palavras-chave is , case ou ou out surge durante a análise do primeiro elemento de um literal de tupla (nesse caso, os tokens são precedidos por ( ou : e o identificador é seguido por um , ) ou um elemento subsequente de um literal de tupla.Those contexts are where the sequence of tokens being disambiguated is immediately preceded by one of the keywords is, case, or out, or arises while parsing the first element of a tuple literal (in which case the tokens are preceded by ( or : and the identifier is followed by a ,) or a subsequent element of a tuple literal.

Regra de desambiguidade modificadaModified disambiguation rule

A regra de desambiguidade revisada seria algo assimThe revised disambiguation rule would be something like this

Se uma sequência de tokens puder ser analisada (no contexto) como um nome simples (§ 7.6.3), acesso de membro (§ 7.6.5) ou acesso de membro de ponteiro (§ 18.5.2) terminando com uma lista de argumentos de tipo (§ 4.4.1), o token imediatamente após o token de fechamento > é examinado, para ver se ele estáIf a sequence of tokens can be parsed (in context) as a simple-name (§7.6.3), member-access (§7.6.5), or pointer-member-access (§18.5.2) ending with a type-argument-list (§4.4.1), the token immediately following the closing > token is examined, to see if it is

  • Um de ( ) ] } : ; , . ? == != | ^ && || & [ ; ouOne of ( ) ] } : ; , . ? == != | ^ && || & [; or
  • Um dos operadores relacionais < > <= >= is as ; ouOne of the relational operators < > <= >= is as; or
  • Uma palavra-chave de consulta contextual que aparece dentro de uma expressão de consulta; orA contextual query keyword appearing inside a query expression; or
  • Em determinados contextos, tratamos o identificador como um token de ambiguidade.In certain contexts, we treat identifier as a disambiguating token. Esses contextos são onde a sequência de tokens que estão sendo desambiguados é imediatamente precedida por uma das palavras-chave is , case ou out ou surge durante a análise do primeiro elemento de um literal de tupla (nesse caso, os tokens são precedidos por ( ou : e o identificador é seguido por um , ) ou um elemento subsequente de um literal de tupla.Those contexts are where the sequence of tokens being disambiguated is immediately preceded by one of the keywords is, case or out, or arises while parsing the first element of a tuple literal (in which case the tokens are preceded by ( or : and the identifier is followed by a ,) or a subsequent element of a tuple literal.

Se o seguinte token estiver entre essa lista ou um identificador nesse contexto, a lista de argumentos de tipo será mantida como parte do nome simples, acesso de membro ou ponteiro-acesso de membro e qualquer outra análise possível da sequência de tokens será descartada.If the following token is among this list, or an identifier in such a context, then the type-argument-list is retained as part of the simple-name, member-access or pointer-member-access and any other possible parse of the sequence of tokens is discarded. Caso contrário, a lista de argumentos de tipo não será considerada como parte do nome simples, acesso de membro ou acesso de membro de ponteiro, mesmo que não haja nenhuma outra análise possível da sequência de tokens.Otherwise, the type-argument-list is not considered to be part of the simple-name, member-access or pointer-member-access, even if there is no other possible parse of the sequence of tokens. Observe que essas regras não são aplicadas ao analisar uma lista de argumentos de tipo em um namespace-ou-Type-name (§ 3,8).Note that these rules are not applied when parsing a type-argument-list in a namespace-or-type-name (§3.8).

Alterações recentes devido a esta propostaBreaking changes due to this proposal

Nenhuma alteração significativa é conhecida devido a essa regra de Desambigüidade proposta.No breaking changes are known due to this proposed disambiguation rule.

Exemplos interessantesInteresting examples

Aqui estão alguns resultados interessantes dessas regras de Desambigüidade:Here are some interesting results of these disambiguation rules:

A expressão (A < B, C > D) é uma tupla com dois elementos, cada uma comparando.The expression (A < B, C > D) is a tuple with two elements, each a comparison.

A expressão (A<B,C> D, E) é uma tupla com dois elementos, o primeiro deles é uma expressão de declaração.The expression (A<B,C> D, E) is a tuple with two elements, the first of which is a declaration expression.

A invocação M(A < B, C > D, E) tem três argumentos.The invocation M(A < B, C > D, E) has three arguments.

A invocação M(out A<B,C> D, E) tem dois argumentos, o primeiro é uma out declaração.The invocation M(out A<B,C> D, E) has two arguments, the first of which is an out declaration.

A expressão e is A<B> C usa uma expressão de declaração.The expression e is A<B> C uses a declaration expression.

O rótulo case case A<B> C: usa uma expressão de declaração.The case label case A<B> C: uses a declaration expression.

Alguns exemplos de correspondência de padrõesSome examples of pattern matching

Is-AsIs-As

Podemos substituir o idiomaWe can replace the idiom

var v = expr as Type;   
if (v != null) {
    // code using v
}

Com um pouco mais conciso e diretoWith the slightly more concise and direct

if (expr is Type v) {
    // code using v
}

Testando anulávelTesting nullable

Podemos substituir o idiomaWe can replace the idiom

Type? v = x?.y?.z;
if (v.HasValue) {
    var value = v.GetValueOrDefault();
    // code using value
}

Com um pouco mais conciso e diretoWith the slightly more concise and direct

if (x?.y?.z is Type value) {
    // code using value
}

Simplificação aritméticaArithmetic simplification

Suponha que definimos um conjunto de tipos recursivos para representar expressões (por uma proposta separada):Suppose we define a set of recursive types to represent expressions (per a separate proposal):

abstract class Expr;
class X() : Expr;
class Const(double Value) : Expr;
class Add(Expr Left, Expr Right) : Expr;
class Mult(Expr Left, Expr Right) : Expr;
class Neg(Expr Value) : Expr;

Agora, podemos definir uma função para computar a derivada (não reduzida) de uma expressão:Now we can define a function to compute the (unreduced) derivative of an expression:

Expr Deriv(Expr e)
{
  switch (e) {
    case X(): return Const(1);
    case Const(*): return Const(0);
    case Add(var Left, var Right):
      return Add(Deriv(Left), Deriv(Right));
    case Mult(var Left, var Right):
      return Add(Mult(Deriv(Left), Right), Mult(Left, Deriv(Right)));
    case Neg(var Value):
      return Neg(Deriv(Value));
  }
}

Um simplificador de expressão demonstra padrões posicionais:An expression simplifier demonstrates positional patterns:

Expr Simplify(Expr e)
{
  switch (e) {
    case Mult(Const(0), *): return Const(0);
    case Mult(*, Const(0)): return Const(0);
    case Mult(Const(1), var x): return Simplify(x);
    case Mult(var x, Const(1)): return Simplify(x);
    case Mult(Const(var l), Const(var r)): return Const(l*r);
    case Add(Const(0), var x): return Simplify(x);
    case Add(var x, Const(0)): return Simplify(x);
    case Add(Const(var l), Const(var r)): return Const(l+r);
    case Neg(Const(var k)): return Const(-k);
    default: return e;
  }
}