Critères spéciaux pour C# 7Pattern Matching for C# 7

Les extensions de critères spéciaux pour C# permettent de nombreux avantages des types de données algébriques et des critères spéciaux des langages fonctionnels, mais d’une façon qui s’intègre parfaitement au langage sous-jacent.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. Les fonctionnalités de base sont : les types d’enregistrements, qui sont des types dont la signification sémantique est décrite par la forme des données ; et les critères spéciaux, qui sont un nouveau formulaire d’expression qui permet une décomposition à plusieurs niveaux extrêmement concise de ces types de données.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. Les éléments de cette approche sont inspirés par les fonctionnalités associées dans les langages de programmation F # et Scala.Elements of this approach are inspired by related features in the programming languages F# and Scala.

Expression isIs expression

L' is opérateur est étendu pour tester une expression par rapport à un modèle.The is operator is extended to test an expression against a pattern.

relational_expression
    : relational_expression 'is' pattern
    ;

Cette forme de relational_expression s’ajoute aux formulaires existants dans la spécification C#.This form of relational_expression is in addition to the existing forms in the C# specification. Il s’agit d’une erreur de compilation si le relational_expression à gauche du is jeton ne désigne pas une valeur ou n’a pas de type.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.

Chaque identificateur du modèle introduit une nouvelle variable locale qui est définitivement assignée une fois que l' is opérateur a la true valeur (c’est-à-dire assignée de manière définitive quand la valeur est 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).

Remarque : il existe techniquement une ambiguïté entre le type d’un is-expression et constant_pattern, qui peut être une analyse valide d’un identificateur qualifié.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. Nous essayons de la lier en tant que type pour la compatibilité avec les versions précédentes du langage. en cas d’échec, nous allons le résoudre comme nous le faisons dans d’autres contextes, à la première chose trouvée (qui doit être une constante ou un type).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). Cette ambiguïté est uniquement présente sur le côté droit d’une is expression.This ambiguity is only present on the right-hand-side of an is expression.

ModèlesPatterns

Les modèles sont utilisés dans l' is opérateur et dans un switch_Statement pour exprimer la forme des données par rapport à laquelle les données entrantes doivent être comparées.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. Les modèles peuvent être récursifs afin que certaines parties des données puissent être comparées à des sous-modèles.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
    ;

Remarque : il existe techniquement une ambiguïté entre le type d’un is-expression et constant_pattern, qui peut être une analyse valide d’un identificateur qualifié.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. Nous essayons de la lier en tant que type pour la compatibilité avec les versions précédentes du langage. en cas d’échec, nous allons le résoudre comme nous le faisons dans d’autres contextes, à la première chose trouvée (qui doit être une constante ou un type).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). Cette ambiguïté est uniquement présente sur le côté droit d’une is expression.This ambiguity is only present on the right-hand-side of an is expression.

Modèle de déclarationDeclaration pattern

Le declaration_pattern teste qu’une expression est d’un type donné et la convertit en ce type si le test a la valeur.The declaration_pattern both tests that an expression is of a given type and casts it to that type if the test succeeds. Si le simple_designation est un identificateur, il introduit une variable locale du type donné nommé par l’identificateur donné.If the simple_designation is an identifier, it introduces a local variable of the given type named by the given identifier. Cette variable locale est assignée définitivement lorsque le résultat de l’opération de correspondance de modèle est true.That local variable is definitely assigned when the result of the pattern-matching operation is true.

declaration_pattern
    : type simple_designation
    ;

La sémantique d’exécution de cette expression est qu’elle teste le type de Runtime de l’opérande de relational_expression gauche par rapport au type dans le modèle.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. S’il s’agit d’un type de Runtime (ou d’un sous-type), le résultat de is operator est true .If it is of that runtime type (or some subtype), the result of the is operator is true. Elle déclare une nouvelle variable locale nommée par l' identificateur auquel est affectée la valeur de l’opérande de gauche lorsque le résultat est 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.

Certaines combinaisons de type statique du côté gauche et du type donné sont considérées comme incompatibles et entraînent une erreur au moment de la compilation.Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. Une valeur de type static E est dite compatible avec le modèle T s’il existe une conversion d’identité, une conversion de référence implicite, une conversion boxing, une conversion de référence explicite ou une conversion unboxing de E en 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. Il s’agit d’une erreur de compilation si une expression de type E n’est pas compatible avec le type de modèle dans un modèle de type auquel elle est associée.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.

Remarque : en C# 7,1, nous étendons cela pour permettre une opération de critères spéciaux si le type d’entrée ou le type T est un type ouvert.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. Ce paragraphe est remplacé par ce qui suit :This paragraph is replaced by the following:

Certaines combinaisons de type statique du côté gauche et du type donné sont considérées comme incompatibles et entraînent une erreur au moment de la compilation.Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. Une valeur de type statique E est dite compatible avec le type T s’il existe une conversion d’identité, une conversion de référence implicite, une conversion boxing, une conversion de référence explicite ou une conversion unboxing de E en T , ou si E ou T est un type ouvert.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. Il s’agit d’une erreur de compilation si une expression de type E n’est pas compatible avec le type de modèle dans un modèle de type auquel elle est associée.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.

Le modèle de déclaration est utile pour effectuer des tests de type au moment de l’exécution de types référence et remplace l’idiomeThe 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 }

Avec un peu plus concisWith the slightly more concise

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

Il s’agit d’une erreur si le type est un type valeur Nullable.It is an error if type is a nullable value type.

Le modèle de déclaration peut être utilisé pour tester des valeurs de types Nullable : une valeur de type Nullable<T> (ou un boxed T ) correspond à un modèle de type T2 id si la valeur n’est pas null et que le type de T2 est T , ou un type de base ou une interface 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. Par exemple, dans le fragment de codeFor example, in the code fragment

int? x = 3;
if (x is int v) { // code using v }

La condition de l' if instruction est true au moment de l’exécution et la variable v contient la valeur 3 du type int dans le bloc.The condition of the if statement is true at runtime and the variable v holds the value 3 of type int inside the block.

Modèle de constanteConstant pattern

constant_pattern
    : shift_expression
    ;

Un modèle de constante teste la valeur d’une expression par rapport à une valeur constante.A constant pattern tests the value of an expression against a constant value. La constante peut être une expression constante, telle qu’un littéral, le nom d’une variable déclarée const , une constante d’énumération ou une typeof expression.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.

Si les deux e et c sont des types intégraux, le modèle est considéré comme mis en correspondance si le résultat de l’expression e == c est 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.

Dans le cas contraire, le modèle est considéré comme une correspondance si object.Equals(e, c) retourne true .Otherwise the pattern is considered matching if object.Equals(e, c) returns true. Dans ce cas, il s’agit d’une erreur au moment de la compilation si le type statique de e n’est pas compatible avec le type de la 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.

Modèle varVar pattern

var_pattern
    : 'var' simple_designation
    ;

Une expression e correspond à un var_pattern toujours.An expression e matches a var_pattern always. En d’autres termes, une correspondance avec un modèle var est toujours réussie.In other words, a match to a var pattern always succeeds. Si le simple_designation est un identificateur, alors, au moment de l’exécution, la valeur de e est liée à une variable locale nouvellement introduite.If the simple_designation is an identifier, then at runtime the value of e is bound to a newly introduced local variable. Le type de la variable locale est le type statique de e.The type of the local variable is the static type of e.

Il s’agit d’une erreur si le nom est var lié à un type.It is an error if the name var binds to a type.

Instruction switchSwitch statement

L' switch instruction est étendue pour sélectionner pour l’exécution le premier bloc ayant un modèle associé qui correspond à l' expression de switch.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
    ;

L’ordre dans lequel les modèles sont mis en correspondance n’est pas défini.The order in which patterns are matched is not defined. Un compilateur est autorisé à faire correspondre les modèles dans le désordre et à réutiliser les résultats des modèles déjà associés pour calculer le résultat de la correspondance d’autres modèles.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.

Si une protection de casse est présente, son expression est de type bool .If a case-guard is present, its expression is of type bool. Elle est évaluée comme une condition supplémentaire qui doit être satisfaite pour que le cas soit considéré comme respecté.It is evaluated as an additional condition that must be satisfied for the case to be considered satisfied.

Il s’agit d’une erreur si un switch_label peut n’avoir aucun effet au moment de l’exécution, car son modèle est inclus dans les cas précédents.It is an error if a switch_label can have no effect at runtime because its pattern is subsumed by previous cases. [TODO : nous devrions être plus précis concernant les techniques que le compilateur est tenu d’utiliser pour atteindre ce jugement.][TODO: We should be more precise about the techniques the compiler is required to use to reach this judgment.]

Une variable de modèle déclarée dans un switch_label est assignée de manière définitive dans son bloc cas si et uniquement si ce bloc de cas contient précisément une 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 : nous devons spécifier quand un bloc switch est accessible.][TODO: We should specify when a switch block is reachable.]

Étendue des variables de modèleScope of pattern variables

La portée d’une variable déclarée dans un modèle est la suivante :The scope of a variable declared in a pattern is as follows:

  • Si le modèle est une étiquette case, alors l’étendue de la variable est le bloc case.If the pattern is a case label, then the scope of the variable is the case block.

Dans le cas contraire, la variable est déclarée dans une expression is_pattern , et sa portée est basée sur la construction qui englobe immédiatement l’expression contenant l’expression is_pattern comme suit :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:

  • Si l’expression se trouve dans une expression lambda avec une expression, son étendue est le corps de l’expression lambda.If the expression is in an expression-bodied lambda, its scope is the body of the lambda.
  • Si l’expression se trouve dans une méthode ou une propriété à expression nulle, son étendue est le corps de la méthode ou de la propriété.If the expression is in an expression-bodied method or property, its scope is the body of the method or property.
  • Si l’expression est dans une when clause d’une catch clause, sa portée est cette catch clause.If the expression is in a when clause of a catch clause, its scope is that catch clause.
  • Si l’expression se trouve dans un iteration_statement, son étendue est simplement cette instruction.If the expression is in an iteration_statement, its scope is just that statement.
  • Sinon, si l’expression est dans une autre forme d’instruction, son étendue est l’étendue contenant l’instruction.Otherwise if the expression is in some other statement form, its scope is the scope containing the statement.

Dans le but de déterminer l’étendue, un embedded_statement est considéré comme étant dans sa propre étendue.For the purpose of determining the scope, an embedded_statement is considered to be in its own scope. Par exemple, la grammaire d’un if_statement estFor example, the grammar for an if_statement is

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

Par conséquent, si l’instruction contrôlée d’un if_statement déclare une variable de modèle, sa portée est limitée à ce 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);

Dans ce cas, la portée de z est l’instruction incorporée M(y is var z); .In this case the scope of z is the embedded statement M(y is var z);.

D’autres cas sont des erreurs pour d’autres raisons (par exemple, dans la valeur par défaut d’un paramètre ou un attribut, les deux étant une erreur parce que ces contextes nécessitent une expression 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).

En C# 7,3, nous avons ajouté les contextes suivants dans lesquels une variable de modèle peut être déclarée :In C# 7.3 we added the following contexts in which a pattern variable may be declared:

  • Si l’expression se trouve dans un initialiseur de constructeur, sa portée est l' initialiseur de constructeur et le corps du constructeur.If the expression is in a constructor initializer, its scope is the constructor initializer and the constructor's body.
  • Si l’expression se trouve dans un initialiseur de champ, son étendue est le equals_value_clause dans lequel elle apparaît.If the expression is in a field initializer, its scope is the equals_value_clause in which it appears.
  • Si l’expression se trouve dans une clause de requête spécifiée pour être convertie dans le corps d’une expression lambda, son étendue est uniquement cette expression.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.

Modifications apportées à la désambiguïsation syntaxiqueChanges to syntactic disambiguation

Il existe des situations impliquant des génériques où la grammaire C# est ambiguë et la spécification du langage indique comment résoudre ces ambiguïtés :There are situations involving generics where the C# grammar is ambiguous, and the language spec says how to resolve those ambiguities:

ambiguïtés de la grammaire 7.6.5.27.6.5.2 Grammar ambiguities

Les productions pour le nom simple (§ 7.6.3) et l' accès aux membres (§ 7.6.5) peuvent donner lieu à des ambiguïtés dans la grammaire des expressions.The productions for simple-name (§7.6.3) and member-access (§7.6.5) can give rise to ambiguities in the grammar for expressions. Par exemple, l’instruction suivante :For example, the statement:

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

peut être interprété comme un appel à F avec deux arguments, G < A et B > (7) .could be interpreted as a call to F with two arguments, G < A and B > (7). Elle peut également être interprétée comme un appel à F avec un argument, qui est un appel à une méthode générique G avec deux arguments de type et un argument normal.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.

Si une séquence de jetons peut être analysée (en contexte) en tant que nom simple (§ 7.6.3), accès aux membres (§ 7.6.5) ou accès aux membres de pointeurs (§ 18.5.2) se terminant par un type-argument-List (§ 4.4.1), le jeton qui suit immédiatement le jeton de fermeture > est examiné.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. S’il s’agit de l’un desIf it is one of

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

le type-argument-List est conservé dans le cadre du simple-name, member-Access ou pointer-Member-Access et toute autre analyse possible de la séquence de jetons est ignorée.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. Dans le cas contraire, la liste d’arguments de type n’est pas considérée comme faisant partie du simple-name, member-Access ou > pointer-Member-Access, même s’il n’y a aucune autre analyse possible de la séquence de jetons.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. Notez que ces règles ne sont pas appliquées lors de l’analyse d’un type-argument-List dans un espace de noms ou un nom de type (§ 3,8).Note that these rules are not applied when parsing a type-argument-list in a namespace-or-type-name (§3.8). L'instructionThe statement

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

, conformément à cette règle, sera interprété comme un appel à F avec un argument, qui est un appel à une méthode générique G avec deux arguments de type et un argument normal.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. Les instructionsThe statements

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

est interprété comme un appel à F avec deux arguments.will each be interpreted as a call to F with two arguments. L'instructionThe statement

x = F < A > +y;

est interprété comme un opérateur inférieur à, supérieur à et opérateur unaire plus, comme si l’instruction avait été écrite, et x = (F < A) > (+y) non comme un simple-name avec un type-argument-List suivi d’un opérateur binaire 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. Dans l’instructionIn the statement

x = y is C<T> + z;

les jetons C<T> sont interprétés comme un nom de type ou d’espace de noms avec un type-argument-List.the tokens C<T> are interpreted as a namespace-or-type-name with a type-argument-list.

Il y a un certain nombre de modifications introduites dans C# 7 qui rendent ces règles de désambiguïsation ne suffisent plus pour gérer la complexité du langage.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.

Déclarations de variables outOut variable declarations

Il est désormais possible de déclarer une variable dans un argument out :It is now possible to declare a variable in an out argument:

M(out Type name);

Toutefois, le type peut être générique :However, the type may be generic:

M(out A<B> name);

Étant donné que la grammaire de la langue de l’argument utilise l' expression, ce contexte est soumis à la règle de désambiguïsation.Since the language grammar for the argument uses expression, this context is subject to the disambiguation rule. Dans ce cas, la fermeture > est suivie d’un identificateur, qui n’est pas l’un des jetons qui lui permet d’être traité comme un type-argument-List.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. Je propose donc d' Ajouter l' identificateur à l’ensemble de jetons qui déclenche la désambiguïsation pour une liste d’arguments de type.I therefore propose to add identifier to the set of tokens that triggers the disambiguation to a type-argument-list.

Déclarations de tuples et de déconstructionTuples and deconstruction declarations

Un littéral de tuple s’exécute exactement dans le même problème.A tuple literal runs into exactly the same issue. Considérer l’expression de TupleConsider the tuple expression

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

Dans les anciennes règles C# 6 pour l’analyse d’une liste d’arguments, celle-ci est interprétée comme un tuple avec quatre éléments, à partir de A < B la première.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. Toutefois, lorsque cela apparaît à gauche d’une déconstruction, nous voulons que la désambiguation soit déclenchée par le jeton d' identificateur comme décrit ci-dessus :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;

Il s’agit d’une déclaration de déconstruction qui déclare deux variables, la première étant de type A<B,C> et nommée D .This is a deconstruction declaration which declares two variables, the first of which is of type A<B,C> and named D. En d’autres termes, le littéral de tuple contient deux expressions, chacune d’elles étant une expression de déclaration.In other words, the tuple literal contains two expressions, each of which is a declaration expression.

Pour simplifier la spécification et le compilateur, je propose que ce littéral de tuple soit analysé comme un tuple à deux éléments à chaque fois qu’il apparaît (qu’il apparaisse ou non sur le côté gauche d’une assignation).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). Il s’agit d’un résultat naturel de la désambiguïsation qui est décrite dans la section précédente.That would be a natural result of the disambiguation described in the previous section.

Critères spéciauxPattern-matching

Les critères spéciaux présentent un nouveau contexte dans lequel l’ambiguïté de type expression se produit.Pattern matching introduces a new context where the expression-type ambiguity arises. Précédemment, le côté droit d’un is opérateur était un type.Previously the right-hand-side of an is operator was a type. À présent, il peut s’agir d’un type ou d’une expression, et s’il s’agit d’un type, il peut être suivi d’un identificateur.Now it can be a type or expression, and if it is a type it may be followed by an identifier. Cela peut, techniquement, modifier la signification du code existant :This can, technically, change the meaning of existing code:

var x = e is T < A > B;

Cela peut être analysé dans les règles C# 6 commeThis could be parsed under C#6 rules as

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

mais sous les règles C# 7 (avec la désambiguïsation proposée ci-dessus) sont analysées commebut under under C#7 rules (with the disambiguation proposed above) would be parsed as

var x = e is T<A> B;

qui déclare une variable B de type T<A> .which declares a variable B of type T<A>. Heureusement, les compilateurs natif et Roslyn ont un bogue qui leur donne une erreur de syntaxe sur le code C# 6.Fortunately, the native and Roslyn compilers have a bug whereby they give a syntax error on the C#6 code. Par conséquent, cette modification avec rupture particulière n’est pas un problème.Therefore this particular breaking change is not a concern.

La mise en correspondance de modèle introduit des jetons supplémentaires qui doivent conduire la résolution d’ambiguïté à la sélection d’un type.Pattern-matching introduces additional tokens that should drive the ambiguity resolution toward selecting a type. Les exemples suivants de code C# 6 valide existant seraient rompus sans règles de désambiguïsation supplémentaires :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>[];               // [

Proposition de modification de la règle de désambiguïsationProposed change to the disambiguation rule

Je propose de modifier la spécification pour modifier la liste des jetons disambiguating à partir deI propose to revise the specification to change the list of disambiguating tokens from

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

toto

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

Et, dans certains contextes, nous traitons l' identificateur en tant que jeton disambiguating.And, in certain contexts, we treat identifier as a disambiguating token. Ces contextes sont l’endroit où la séquence de jetons enlevée est immédiatement précédée de l’un des mots clés is , case , ou out , ou se présente lors de l’analyse du premier élément d’un littéral de Tuple (auquel cas les jetons sont précédés de ( ou : et l’identificateur est suivi d’un , ) ou d’un élément suivant d’un littéral de Tuple.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.

Règle de désambiguïté modifiéeModified disambiguation rule

La règle de désambiguïté révisée serait semblable à celle-ciThe revised disambiguation rule would be something like this

Si une séquence de jetons peut être analysée (en contexte) en tant que nom simple (§ 7.6.3), accès aux membres (§ 7.6.5) ou accès aux membres de pointeurs (§ 18.5.2) se terminant par un type-argument-List (§ 4.4.1), le jeton qui suit immédiatement le jeton de fermeture > est examiné pour voir s’il estIf 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

  • Un de ( ) ] } : ; , . ? == != | ^ && || & [ ; ouOne of ( ) ] } : ; , . ? == != | ^ && || & [; or
  • Un des opérateurs relationnels < > <= >= is as ; ouOne of the relational operators < > <= >= is as; or
  • Mot clé de requête contextuelle apparaissant à l’intérieur d’une expression de requête ; niA contextual query keyword appearing inside a query expression; or
  • Dans certains contextes, nous traitons l' identificateur en tant que jeton disambiguating.In certain contexts, we treat identifier as a disambiguating token. Ces contextes sont là où la séquence de jetons enlevée est immédiatement précédée de l’un des mots clés is , case ou, ou se présente lors de out l’analyse du premier élément d’un littéral de Tuple (auquel cas les jetons sont précédés de ( ou : et l’identificateur est suivi d’un , ) ou d’un élément suivant d’un littéral de Tuple.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.

Si le jeton suivant figure parmi cette liste, ou un identificateur dans un tel contexte, le type-argument-List est conservé dans le cadre du simple-name, de l' accès aux membres ou du pointeur-Member Access , et toute autre analyse possible de la séquence de jetons est ignorée.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. Dans le cas contraire, la liste d’arguments de type n’est pas considérée comme faisant partie du simple-name, d' accès aux membres ou d' accès aux membres du pointeur, même s’il n’y a pas d’autre analyse possible de la séquence de jetons.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. Notez que ces règles ne sont pas appliquées lors de l’analyse d’un type-argument-List dans un espace de noms ou un nom de type (§ 3,8).Note that these rules are not applied when parsing a type-argument-list in a namespace-or-type-name (§3.8).

Modifications avec rupture en raison de cette propositionBreaking changes due to this proposal

Aucune modification avec rupture n’est connue en raison de cette règle de désambiguïsation d’ambiguïté proposée.No breaking changes are known due to this proposed disambiguation rule.

Exemples intéressantsInteresting examples

Voici quelques résultats intéressants de ces règles de désambiguïsation :Here are some interesting results of these disambiguation rules:

L’expression (A < B, C > D) est un tuple avec deux éléments, chacun étant une comparaison.The expression (A < B, C > D) is a tuple with two elements, each a comparison.

L’expression (A<B,C> D, E) est un tuple avec deux éléments, le premier étant une expression de déclaration.The expression (A<B,C> D, E) is a tuple with two elements, the first of which is a declaration expression.

L’appel M(A < B, C > D, E) a trois arguments.The invocation M(A < B, C > D, E) has three arguments.

L’appel M(out A<B,C> D, E) a deux arguments, le premier étant une out déclaration.The invocation M(out A<B,C> D, E) has two arguments, the first of which is an out declaration.

L’expression e is A<B> C utilise une expression de déclaration.The expression e is A<B> C uses a declaration expression.

L’étiquette case case A<B> C: utilise une expression de déclaration.The case label case A<B> C: uses a declaration expression.

Exemples de critères spéciauxSome examples of pattern matching

Is-AsIs-As

Nous pouvons remplacer l’idiomeWe can replace the idiom

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

Avec un peu plus concis et directWith the slightly more concise and direct

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

Tester la valeur nullTesting nullable

Nous pouvons remplacer l’idiomeWe can replace the idiom

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

Avec un peu plus concis et directWith the slightly more concise and direct

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

Simplification arithmétiqueArithmetic simplification

Supposons que nous définissons un ensemble de types récursifs pour représenter des expressions (selon une proposition distincte) :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;

Nous pouvons maintenant définir une fonction pour calculer la dérivée (non réduite) d’une expression :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));
  }
}

Une expression simplifie les modèles positionnels :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;
  }
}