C# 7 のパターンマッチングPattern Matching for C# 7

C# のパターン一致拡張機能を使用すると、代数データ型と関数型言語のパターンマッチングの多くの利点が得られますが、基になる言語のようにスムーズに統合されます。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. 基本的な機能は、 レコード型です。これは、データの形によって記述される意味を持つ型です。また、パターンマッチングは、これらのデータ型を大幅に簡潔に分解できる新しい式の形式です。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. このアプローチの要素は、プログラミング言語 F #スケール aの関連機能によって実現されています。Elements of this approach are inspired by related features in the programming languages F# and Scala.

Is 式Is expression

is演算子は、パターンに対して式をテストするために拡張されています。The is operator is extended to test an expression against a pattern.

relational_expression
    : relational_expression 'is' pattern
    ;

この形式の relational_expression は、C# 仕様の既存のフォームに追加されています。This form of relational_expression is in addition to the existing forms in the C# specification. トークンの左側の relational_expressionis 値を指定していないか、型を持たない場合、コンパイル時エラーになります。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.

パターンのすべての識別子は、演算子の後に確実に代入される新しいローカル変数を導入します is (つまり、true の場合は確実に割り当てられます true )。 definitely assigned when trueEvery 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).

注: と constant_pattern のの間には技術的にあいまいな点がありますが is-expression 、どちらも、修飾された識別子の有効な解析である可能性があります。 constant_patternNote: 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. 以前のバージョンの言語との互換性のために、型としてバインドしようとしています。失敗した場合にのみ、他のコンテキストの場合と同様に、最初に見つかったもの (定数または型のいずれかである必要があります) に解決されます。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). このあいまいさは、式の右辺にのみ存在し is ます。This ambiguity is only present on the right-hand-side of an is expression.

パターンPatterns

パターンは、 is 入力データの比較対象となるデータの構造を表すために、演算子と switch_statement 内で使用されます。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. パターンは再帰的であるため、データの一部がサブパターンと照合される可能性があります。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
    ;

注: と constant_pattern のの間には技術的にあいまいな点がありますが is-expression 、どちらも、修飾された識別子の有効な解析である可能性があります。 constant_patternNote: 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. 以前のバージョンの言語との互換性のために、型としてバインドしようとしています。失敗した場合にのみ、他のコンテキストの場合と同様に、最初に見つかったもの (定数または型のいずれかである必要があります) に解決されます。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). このあいまいさは、式の右辺にのみ存在し is ます。This ambiguity is only present on the right-hand-side of an is expression.

宣言パターンDeclaration pattern

Declaration_patternは、式が特定の型であるかどうかをテストし、テストが成功した場合はその型にキャストします。The declaration_pattern both tests that an expression is of a given type and casts it to that type if the test succeeds. Simple_designationが識別子の場合は、指定された識別子によって指定された型のローカル変数を導入します。If the simple_designation is an identifier, it introduces a local variable of the given type named by the given identifier. このローカル変数は、パターン一致操作の結果が true の場合に 確実に割り当てら れます。That local variable is definitely assigned when the result of the pattern-matching operation is true.

declaration_pattern
    : type simple_designation
    ;

この式のランタイムセマンティックは、パターンのに対して左辺のrelational_expressionオペランドのランタイム型をテストすることです。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. そのランタイム型 (または一部のサブタイプ) の場合、の結果はになり is operator true ます。If it is of that runtime type (or some subtype), the result of the is operator is true. これは、結果がの場合に左側のオペランドの値が割り当てられた 識別子 によって指定された新しいローカル変数を宣言し 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.

左側の静的な型と指定された型の特定の組み合わせは互換性がないと見なされ、コンパイル時エラーが発生します。Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. 静的な型の値は、 E id pattern compatible T 変換、暗黙の参照変換、ボックス化変換、明示的な参照変換、またはからへのアンボックス変換のいずれかが存在する場合、型とのパターン互換性があると言われ E 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. 型の式 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.

注: C# 7.1 で は、入力型または型 T がオープン型である場合に、パターンマッチング操作を許可するようにこれを拡張します。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. この段落は、次のように置き換えられます。This paragraph is replaced by the following:

左側の静的な型と指定された型の特定の組み合わせは互換性がないと見なされ、コンパイル時エラーが発生します。Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. 静的型の値は、 E pattern compatible T id 変換、暗黙の参照変換、ボックス変換、明示的な参照変換、またはからへのボックス化変換、または E T ** E 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, or if either E or T is an open type. 型の式 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.

宣言パターンは、参照型のランタイム型テストを実行する場合に便利です。The 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 }

少し簡潔にWith the slightly more concise

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

が null 許容値型の場合、エラーになります。It is an error if type is a nullable value type.

宣言パターンは、null 許容型の値をテストするために使用できます。 Nullable<T> T T2 id 値が null 以外で、の型 T2 がである場合 T 、またはの基本型またはインターフェイス 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. たとえば、コード片でFor example, in the code fragment

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

ステートメントの条件 iftrue 実行時で、変数は v ブロック内の型の値を保持し 3 int ます。The condition of the if statement is true at runtime and the variable v holds the value 3 of type int inside the block.

定数パターンConstant pattern

constant_pattern
    : shift_expression
    ;

定数パターンは、定数値に対して式の値をテストします。A constant pattern tests the value of an expression against a constant value. 定数には、リテラル、宣言された変数の名前、列挙定数、式など、任意の定数式を指定でき const typeof ます。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.

Ecの両方が整数型の場合、式の結果がの場合、パターンは一致したと見なされ e == c 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.

それ以外の場合は、がを返す場合、パターンは一致と見なされ object.Equals(e, c) true ます。Otherwise the pattern is considered matching if object.Equals(e, c) returns true. この場合、 e の静的な型が定数の型と互換性のある パターン でない場合、コンパイル時エラーになります。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.

Var パターンVar pattern

var_pattern
    : 'var' simple_designation
    ;

e は常に var_pattern と一致します。An expression e matches a var_pattern always. 言い換えると、 var パターン との一致は常に成功します。In other words, a match to a var pattern always succeeds. Simple_designationが識別子の場合は、実行時にeの値が新しく導入されたローカル変数にバインドされます。If the simple_designation is an identifier, then at runtime the value of e is bound to a newly introduced local variable. ローカル変数の型は、 eの静的な型です。The type of the local variable is the static type of e.

名前が型にバインドされている場合、エラーになり var ます。It is an error if the name var binds to a type.

Switch ステートメントSwitch statement

ステートメントは、 switch 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
    ;

パターンが一致する順序は定義されていません。The order in which patterns are matched is not defined. コンパイラは、パターンを順序どおりに一致させ、既に一致したパターンの結果を再利用して、他のパターンとの照合結果を計算することができます。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.

Case ガードが存在する場合、その式は型に bool なります。If a case-guard is present, its expression is of type bool. これは、満たされた場合に満たす必要がある追加の条件として評価されます。It is evaluated as an additional condition that must be satisfied for the case to be considered satisfied.

Switch_labelが実行時に影響を与えない場合、これはエラーになります。これは、そのパターンが前のケースで包括されているためです。It is an error if a switch_label can have no effect at runtime because its pattern is subsumed by previous cases. [TODO: この判断に至るためにコンパイラが使用する必要がある手法について、より正確に指定する必要があります。][TODO: We should be more precise about the techniques the compiler is required to use to reach this judgment.]

Switch_labelで宣言されたパターン変数は、その case ブロックに1つのswitch_labelが含まれている場合にのみ、その case ブロックで確実に割り当てられます。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: スイッチブロック に到達できる場合は、を指定する必要があります。][TODO: We should specify when a switch block is reachable.]

パターン変数のスコープScope of pattern variables

パターンで宣言された変数のスコープは次のとおりです。The scope of a variable declared in a pattern is as follows:

  • パターンが case ラベルの場合、変数のスコープは case ブロックになります。If the pattern is a case label, then the scope of the variable is the case block.

それ以外の場合、変数は is_pattern 式で宣言され、そのスコープは、次のように is_pattern 式を含む式をすぐに囲むコンストラクトに基づいています。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:

  • 式が式形式のラムダ内にある場合、そのスコープはラムダの本体です。If the expression is in an expression-bodied lambda, its scope is the body of the lambda.
  • 式が式形式のメソッドまたはプロパティに含まれている場合、そのスコープはメソッドまたはプロパティの本体です。If the expression is in an expression-bodied method or property, its scope is the body of the method or property.
  • 式が句の句に含まれている場合 when catch 、そのスコープはその catch 句です。If the expression is in a when clause of a catch clause, its scope is that catch clause.
  • 式が iteration_statement内にある場合、そのスコープはそのステートメントにすぎません。If the expression is in an iteration_statement, its scope is just that statement.
  • それ以外の場合、式が他のステートメント形式に含まれていると、そのスコープがステートメントを含むスコープになります。Otherwise if the expression is in some other statement form, its scope is the scope containing the statement.

スコープを決定するために、 embedded_statement は独自のスコープ内にあると見なされます。For the purpose of determining the scope, an embedded_statement is considered to be in its own scope. たとえば、 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
    ;

したがって、 if_statement の制御されたステートメントがパターン変数を宣言している場合、そのスコープはその 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);

この場合、のスコープ z は埋め込みステートメント M(y is var z); です。In this case the scope of z is the embedded statement M(y is var z);.

その他の理由 (たとえば、パラメーターの既定値や属性など) でエラーが発生した場合は、これらのコンテキストには定数式が必要であるため、どちらもエラーになります。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).

C# 7.3 では、 pattern 変数が宣言される次のコンテキストを追加しました。In C# 7.3 we added the following contexts in which a pattern variable may be declared:

  • 式が コンストラクター初期化子内にある場合、そのスコープは コンストラクター初期化子 とコンストラクターの本体になります。If the expression is in a constructor initializer, its scope is the constructor initializer and the constructor's body.
  • 式がフィールド初期化子に含まれている場合、そのスコープは、その式が表示される equals_value_clause になります。If the expression is in a field initializer, its scope is the equals_value_clause in which it appears.
  • ラムダの本体に変換するように指定されているクエリ句に式が含まれている場合、その式のスコープはその式にすぎません。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.

構文のあいまいさを解消するための変更Changes to syntactic disambiguation

ジェネリックには、C# 文法があいまいであるという点があります。言語仕様では、あいまいさを解決する方法が示されています。There are situations involving generics where the C# grammar is ambiguous, and the language spec says how to resolve those ambiguities:

7.6.5.2 文法のあいまい性7.6.5.2 Grammar ambiguities

単純名(7.6.3) とメンバーアクセス(7.6.5 を参照) の生産によって、式の文法があいまいになる可能性があります。The productions for simple-name (§7.6.3) and member-access (§7.6.5) can give rise to ambiguities in the grammar for expressions. たとえば、ステートメントは次のようになります。For example, the statement:

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

F 、2つの引数 (と) を持つの呼び出しとして解釈される可能性があり G < A B > (7) ます。could be interpreted as a call to F with two arguments, G < A and B > (7). または、1つの引数を使用したの呼び出しとして解釈することもでき F ます。これは、 G 2 つの型引数と1つの標準引数を持つジェネリックメソッドの呼び出しです。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.

トークンのシーケンスを 単純な名前 (7.6.3)、 メンバーアクセス (§ 7.6.5)、または ポインターメンバーアクセス (§ 18.5.2) で終わり (4.4.1 を参照) として解析できる場合は、終了トークンの直後にあるトークンが調べられます ( 」を参照) >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. のいずれかである場合If it is one of

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

その後、 型引数リスト は、 単純名メンバーアクセス 、または ポインターメンバーアクセス の一部として保持され、トークンのシーケンスのその他の可能な解析はすべて破棄されます。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. それ以外の場合、トークンのシーケンスを解析できない場合でも、 型引数リスト は、 単純名メンバーアクセス 、または > ポインターメンバーアクセスの一部とは見なされません。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. 名前空間または型名型引数リストを解析する場合、これらの規則は適用されないことに注意してください (3.8 を参照)。Note that these rules are not applied when parsing a type-argument-list in a namespace-or-type-name (§3.8). 次のステートメント、The statement

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

は、この規則に従って、1つの引数を使用したの呼び出しとして解釈されます。これは、2つの F G 型引数と1つの標準引数を持つジェネリックメソッドの呼び出しです。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. ステートメントThe statements

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

は、 F 2 つの引数を持つの呼び出しとして解釈されます。will each be interpreted as a call to F with two arguments. 次のステートメント、The statement

x = F < A > +y;

は、ステートメントが記述されているかのように、小なり演算子、大なり演算子、および単項プラス演算子として解釈され x = (F < A) > (+y) ます。 type-argument-listただし、その後に二項プラス演算子が続く単純な名前ではありません。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. ステートメント内In the statement

x = y is C<T> + z;

トークン C<T> は、型引数リストを持つ名前空間または型名として解釈されます。the tokens C<T> are interpreted as a namespace-or-type-name with a type-argument-list.

C# 7 では、言語の複雑さに対処するためにこれらのあいまいさを解消するための十分な変更がいくつか追加されています。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.

out 変数宣言Out variable declarations

Out 引数で変数を宣言できるようになりました。It is now possible to declare a variable in an out argument:

M(out Type name);

ただし、型はジェネリックである場合があります。However, the type may be generic:

M(out A<B> name);

引数の言語文法では が使用されるため、このコンテキストにはあいまいさの排除規則が適用されます。Since the language grammar for the argument uses expression, this context is subject to the disambiguation rule. この場合、閉じの > 後に 識別子が続きます。この識別子は、 型引数リストとして扱うことを許可するトークンの1つではありません。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. そのため、あいまいさを解消するトークンのセットに識別子を追加すること*type-argument-list* を提案します。I therefore propose to add identifier to the set of tokens that triggers the disambiguation to a type-argument-list.

タプルと分解宣言Tuples and deconstruction declarations

組リテラルは、まったく同じ問題に実行されます。A tuple literal runs into exactly the same issue. タプル式を考えます。Consider the tuple expression

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

以前の C# 6 規則で引数リストを解析する場合は、が最初のから始まる4つの要素を持つタプルとして解析され A < B ます。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. ただし、分解の左側にこれが表示される場合は、前述のように、 識別子 トークンによってトリガーされるあいまいさを解消する必要があります。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;

これは、2つの変数を宣言する分解宣言です。最初の変数は型で、は A<B,C> という名前 D です。This is a deconstruction declaration which declares two variables, the first of which is of type A<B,C> and named D. 言い換えると、組リテラルには2つの式が含まれ、それぞれが宣言式です。In other words, the tuple literal contains two expressions, each of which is a declaration expression.

仕様とコンパイラを簡単にするために、このタプルリテラルを (代入式の左側に表示するかどうかにかかわらず) 任意の場所に2要素のタプルとして解析することを提案します。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). これは、前のセクションで説明した非不明瞭の自然な結果になります。That would be a natural result of the disambiguation described in the previous section.

パターンマッチングPattern-matching

パターンマッチングでは、式型のあいまいさが発生する新しいコンテキストが導入されます。Pattern matching introduces a new context where the expression-type ambiguity arises. 以前は、演算子の右辺は is 型でした。Previously the right-hand-side of an is operator was a type. これで、型または式にすることができます。型である場合は、識別子の後に指定できます。Now it can be a type or expression, and if it is a type it may be followed by an identifier. これにより、技術的には既存のコードの意味を変更できます。This can, technically, change the meaning of existing code:

var x = e is T < A > B;

これは、次のように C# 6 規則で解析できます。This could be parsed under C#6 rules as

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

ただし、C# 7 の規則 (上記のあいまいさを排除したもの) は、として解析されます。but under under C#7 rules (with the disambiguation proposed above) would be parsed as

var x = e is T<A> B;

型の変数を宣言する B T<A>which declares a variable B of type T<A>. さいわい、ネイティブコンパイラと Roslyn コンパイラには、C# 6 コードに構文エラーが発生するというバグがあります。Fortunately, the native and Roslyn compilers have a bug whereby they give a syntax error on the C#6 code. そのため、このような重大な変更は考慮されません。Therefore this particular breaking change is not a concern.

パターンマッチングでは、型の選択に向けたあいまいさの解決を推進する必要がある追加のトークンが導入されます。Pattern-matching introduces additional tokens that should drive the ambiguity resolution toward selecting a type. 次の例では、既存の有効な C# 6 コードは、その他のあいまいさを排除する規則なしに分割されます。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>[];               // [

排除規則に対する変更の提案Proposed change to the disambiguation rule

明確化トークンの一覧を変更するように仕様を修正することを提案します。I propose to revise the specification to change the list of disambiguating tokens from

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

toto

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

また、特定のコンテキストでは、 識別子 を明確化トークンとして扱います。And, in certain contexts, we treat identifier as a disambiguating token. これらのコンテキストは、明確されるトークンのシーケンスが、キーワード、、またはのいずれかの直前にあるか、または is case out タプルリテラルの最初の要素を解析するときに発生します (この場合、トークンの前にまたはが付いていて、識別子の後にが続く)、または ( : , タプルリテラルの後続の要素。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.

変更されたあいまいさの排除ルールModified disambiguation rule

修正されていないあいまいさの規則は次のようになります。The revised disambiguation rule would be something like this

トークンのシーケンスを、 単純な名前 (7.6.3)、 メンバーアクセス (§ 7.6.5)、または ポインターメンバーアクセス (§ 18.5.2) として、 型引数リスト (4.4.1 を参照) として解析できる場合は、最後のトークンの直後にあるトークンが検査され、実行 > されているかどうかが確認されます。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

  • のいずれか ( ) ] } : ; , . ? == != | ^ && || & [ 。またはOne of ( ) ] } : ; , . ? == != | ^ && || & [; or
  • 関係演算子の1つ < > <= >= is as 。またはOne of the relational operators < > <= >= is as; or
  • クエリ式の内部に表示されるコンテキストクエリキーワードもしくはA contextual query keyword appearing inside a query expression; or
  • 特定のコンテキストでは、 識別子 を明確化トークンとして扱います。In certain contexts, we treat identifier as a disambiguating token. これらのコンテキストは、明確されているトークンのシーケンスの前に、またはのいずれかのキーワードが直前にあるか is case 、また out はタプルリテラルの最初の要素を解析するときに発生します (この場合は、トークンの前にまたはが付いていて、識別子の後にが続き ( : , ます)。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.

次のトークンがこのリストまたはこのようなコンテキストの識別子の中にある場合、 型引数リスト単純名メンバーアクセス 、または ポインターメンバーアクセス の一部として保持され、その他のトークンシーケンスの解析はすべて破棄されます。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. それ以外の場合、トークンのシーケンスを解析できない場合でも、 型引数リスト は、 単純名メンバーアクセス 、または ポインターメンバーアクセスの一部とは見なされません。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. 名前空間または型名型引数リストを解析する場合、これらの規則は適用されないことに注意してください (3.8 を参照)。Note that these rules are not applied when parsing a type-argument-list in a namespace-or-type-name (§3.8).

この提案による重大な変更Breaking changes due to this proposal

この修正候補の規則により、互換性に影響する変更は認識されません。No breaking changes are known due to this proposed disambiguation rule.

興味深い例Interesting examples

これらの非不明瞭な規則の興味深い結果を次に示します。Here are some interesting results of these disambiguation rules:

式は (A < B, C > D) 2 つの要素を持つタプルで、それぞれが比較されます。The expression (A < B, C > D) is a tuple with two elements, each a comparison.

式は (A<B,C> D, E) 2 つの要素を持つタプルで、最初の要素は宣言式です。The expression (A<B,C> D, E) is a tuple with two elements, the first of which is a declaration expression.

この呼び出しに M(A < B, C > D, E) は3つの引数があります。The invocation M(A < B, C > D, E) has three arguments.

呼び出しに M(out A<B,C> D, E) は2つの引数があり、最初の引数は out 宣言です。The invocation M(out A<B,C> D, E) has two arguments, the first of which is an out declaration.

この式では、 e is A<B> C 宣言式が使用されます。The expression e is A<B> C uses a declaration expression.

Case ラベルでは、 case A<B> C: 宣言式が使用されます。The case label case A<B> C: uses a declaration expression.

パターンマッチングの例Some examples of pattern matching

Is-As

表現を置き換えることができます。We can replace the idiom

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

少し簡潔で、直接With the slightly more concise and direct

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

テスト (null 許容)Testing nullable

表現を置き換えることができます。We can replace the idiom

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

少し簡潔で、直接With the slightly more concise and direct

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

算術単純化Arithmetic simplification

(別の提案に従って) 式を表す一連の再帰型を定義するとします。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;

これで、式の (縮小されていない) 派生を計算する関数を定義できます。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));
  }
}

位置指定パターンを示す式 simplifier を次に示します。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;
  }
}