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 #Scala中的相關功能所靈感。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. 如果 token 左邊的 relational_expression is 未指定值或沒有類型,就會發生編譯時期錯誤。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) 時明確指派 )。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).

注意:在技術上,和 constant_pattern 中的 類型 之間會有不明確的情況 is-expression ,可能是限定識別碼的有效剖析。 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. 我們會嘗試將其系結為類型,以便與舊版的語言相容;只有當它失敗時,才會在其他內容中解決這種情況, (必須是常數或類型) 。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). 這種不明確的只存在於運算式的右手邊 isThis 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 ,可能是限定識別碼的有效剖析。 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. 我們會嘗試將其系結為類型,以便與舊版的語言相容;只有當它失敗時,才會在其他內容中解決這種情況, (必須是常數或類型) 。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). 這種不明確的只存在於運算式的右手邊 isThis 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 operatortrueIf it is of that runtime type (or some subtype), the result of the is operator is true. 它會宣告以 識別碼 命名的新區域變數,並在結果為時指派左邊運算元的值 trueIt 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 T 如果有身分識別轉換、隱含參考轉換、裝箱轉換、明確參考轉換,或從轉換 E 為,則會將靜態類型的值視為與型別相容的模式 TA 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. 如果類型的運算式不符合與型別模式相容的型別,則會是編譯時期錯誤 EIt 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 中,我們會將它擴充 ,以便在輸入類型或類型為開啟類型時,允許模式比對作業 TNote: 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 T 如果有身分識別轉換、隱含參考轉換、裝箱轉換、明確參考轉換,或從轉換 ET ET 為開放式型 別,則會將靜態型別的值視為與型別相容的模式。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. 如果類型的運算式不符合與型別模式相容的型別,則會是編譯時期錯誤 EIt 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 區塊內保存類型的值 intThe 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 trueIf 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) trueOtherwise 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.

如果名稱系結至類型,就會發生錯誤 varIt is an error if the name var binds to a type.

Switch 陳述式Switch statement

switch 語句會擴充為 select,以選取要執行的第一個區塊,其中有相關聯的模式符合 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.

如果有一個 狀況 成立,則其運算式的類型為 boolIf 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 中宣告的模式變數,在其案例區塊中是絕對指派的,只有在該案例區塊精確包含一個 switch_labelA 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:

  • 如果模式是案例標籤,則變數的範圍是 案例區塊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:

  • 如果運算式是在運算式主體 lambda 中,其範圍就是 lambda 的主體。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_statementSo 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 中,我們新增了 可宣告模式變數的下列內容: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_clauseIf the expression is in a field initializer, its scope is the equals_value_clause in which it appears.
  • 如果運算式是在指定要轉譯成 lambda 主體的查詢子句中,其範圍就只是該運算式。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 用兩個引數(和)的呼叫來解讀 G < A B > (7)could be interpreted as a call to F with two arguments, G < A and B > (7). 或者,您也可以使用一個引數來將它解釋為呼叫 F ,此引數是 G 使用兩個型別引數和一個一般引數呼叫泛型方法。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.

如果可以將內容) 中的 token 序列 (為 簡單名稱 (§ 7.6.3) 、成員存取 (第 7.6.5) ,或以 類型引數清單 結尾的 指標成員存取 (第一個) 第 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

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

然後, 型別引數清單 會保留為 簡單名稱成員存取指標成員存取 的一部分,而且會捨棄任何其他可能的 token 序列剖析。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. 否則,即使沒有其他可能的 token 順序剖析,也不會將 類型引數清單 視為 簡單名稱成員存取 或 > 指標成員存取 的一部分。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));

根據此規則,將會以一個引數的呼叫來解讀,此 F 引數是 G 使用兩個型別引數和一個一般引數呼叫泛型方法。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);

每個都會以兩個引數的呼叫來解讀 Fwill each be interpreted as a call to F with two arguments. 陳述式The statement

x = F < A > +y;

將會被視為小於運算子、大於運算子和一元加號運算子,如同已寫入語句 x = (F < A) > (+y) ,而不是以 類型引數清單 後面接著二元加號運算子的 簡單名稱 來解讀。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. 在此情況下,結尾 > 後面會接著 識別碼,這不是允許將它視為 類型引數清單 的其中一個標記。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. 因此,我建議 識別碼 新增至一組標記,以觸發不區分的 型別引數清單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 規則下,這會剖析為具有四個元素的元組,以 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;

這是宣告兩個變數的解構宣告,第一個是型別 A<B,C> 並命名為 DThis is a deconstruction declaration which declares two variables, the first of which is of type A<B,C> and named D. 換句話說,元組常值包含兩個運算式,每個運算式都是宣告運算式。In other words, the tuple literal contains two expressions, each of which is a declaration expression.

為了簡單起見,我建議將這個元組常值剖析成兩個元素的元組,無論它出現在指派) 的左邊, (。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.

模式比對會引進額外的 token,這些 token 應可推動選取類型的明確解析度。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

我建議修改規格,以變更 disambiguating 權杖的清單I propose to revise the specification to change the list of disambiguating tokens from

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

toto

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

而且,在某些環境中,我們會將 識別碼 視為 disambiguating token。And, in certain contexts, we treat identifier as a disambiguating token. 這些內容是要辨識的 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

如果 token 的序列可以剖析 (于) 簡單名稱的簡單名稱 (§ 7.6.3) 、成員存取 (第 7.6.5) ,或以 類型引數清單 結尾的成員 存取 (第一個) 第一個 4.4.1 (,則會檢查結尾 token 之後的 token > ,以查看其是否為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
  • 其中一個關係運算子 < > <= >= is as ; 或One of the relational operators < > <= >= is as; or
  • 出現在查詢運算式內的內容查詢關鍵字;或A contextual query keyword appearing inside a query expression; or
  • 在某些環境中,我們會將 識別碼 視為 disambiguating token。In certain contexts, we treat identifier as a disambiguating token. 這些內容是要辨識的 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.

如果下列標記是在此清單中,或在這類內容中的識別碼,則 類型引數清單 會保留為 簡單名稱成員存取指標成員存取 的一部分,而且會捨棄任何其他可能的 token 剖析順序。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. 否則,即使沒有其他可能的 token 順序剖析,也不會將 類型引數清單 視為 簡單名稱成員存取 權或成員 指標存取 的一部分。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) 是具有兩個元素的元組,每個都有比較。The expression (A < B, C > D) is a tuple with two elements, each a comparison.

運算式 (A<B,C> D, E) 是具有兩個元素的元組,第一個是宣告運算式。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) 有三個引數。The invocation M(A < B, C > D, E) has three arguments.

調用 M(out A<B,C> D, E) 具有兩個引數,第一個引數是宣告 outThe 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-AsIs-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
}

測試可為 nullTesting 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;

現在,我們可以定義函式來計算運算式的 (unreduced) 衍生: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;
  }
}