運算式Expressions

運算式是一串運算子和運算元。An expression is a sequence of operators and operands. 本章定義語法、運算元和運算子的評估順序,以及運算式的意義。This chapter defines the syntax, order of evaluation of operands and operators, and meaning of expressions.

運算式分類Expression classifications

運算式分類為下列其中一項:An expression is classified as one of the following:

  • 值。A value. 每個值都有關聯型別。Every value has an associated type.
  • 變數。A variable. 每個變數都有相關聯的類型,也就是變數的宣告型別。Every variable has an associated type, namely the declared type of the variable.
  • 命名空間。A namespace. 具有此分類的運算式只能出現在 member_access (成員存取) 的左邊。An expression with this classification can only appear as the left hand side of a member_access (Member access). 在任何其他內容中,分類為命名空間的運算式會導致編譯階段錯誤。In any other context, an expression classified as a namespace causes a compile-time error.
  • 類型。A type. 具有此分類的運算式只能出現在 member_access (成員存取) 的左邊,或做為運算子的運算元 as (as 運算子) 、 is 運算子 (is 運算子) 或 typeof 運算子 (typeof 運算子) 。An expression with this classification can only appear as the left hand side of a member_access (Member access), or as an operand for the as operator (The as operator), the is operator (The is operator), or the typeof operator (The typeof operator). 在任何其他內容中,分類為類型的運算式會導致編譯時期錯誤。In any other context, an expression classified as a type causes a compile-time error.
  • 方法群組,這是成員查閱 (成員查閱) 所產生的一組多載方法。A method group, which is a set of overloaded methods resulting from a member lookup (Member lookup). 方法群組可以有相關聯的實例運算式和關聯的型別引數清單。A method group may have an associated instance expression and an associated type argument list. 叫用實例方法時,評估實例運算式的結果會成為 this (此存取) 所代表的實例。When an instance method is invoked, the result of evaluating the instance expression becomes the instance represented by this (This access). Invocation_expression 的 (調用運算式中允許方法群組) 、 delegate_creation_expression (委派建立運算式) 和做為運算子的左邊,並且可以隱含地轉換為相容的委派類型 (方法群組轉換) 。A method group is permitted in an invocation_expression (Invocation expressions) , a delegate_creation_expression (Delegate creation expressions) and as the left hand side of an is operator, and can be implicitly converted to a compatible delegate type (Method group conversions). 在任何其他內容中,分類為方法群組的運算式會導致編譯時期錯誤。In any other context, an expression classified as a method group causes a compile-time error.
  • Null 常值。A null literal. 具有此分類的運算式可以隱含地轉換成參考型別或可為 null 的型別。An expression with this classification can be implicitly converted to a reference type or nullable type.
  • 匿名函式。An anonymous function. 具有此分類的運算式可以隱含地轉換成相容的委派類型或運算式樹狀架構類型。An expression with this classification can be implicitly converted to a compatible delegate type or expression tree type.
  • 屬性存取。A property access. 每個屬性存取都有相關聯的類型,也就是屬性的型別。Every property access has an associated type, namely the type of the property. 此外,屬性存取可能會有相關聯的實例運算式。Furthermore, a property access may have an associated instance expression. 當叫用存取子 get (set 實例屬性存取的或區塊) 時,評估實例運算式的結果會變成 this (此存取) 所代表的實例。When an accessor (the get or set block) of an instance property access is invoked, the result of evaluating the instance expression becomes the instance represented by this (This access).
  • 事件存取。An event access. 每個事件存取都有相關聯的類型,也就是事件的類型。Every event access has an associated type, namely the type of the event. 此外,事件存取可能會有相關聯的實例運算式。Furthermore, an event access may have an associated instance expression. 事件存取可能會顯示為和運算子的左運算元 +=-= (事件指派) 。An event access may appear as the left hand operand of the += and -= operators (Event assignment). 在任何其他內容中,分類為事件存取權的運算式會導致編譯時期錯誤。In any other context, an expression classified as an event access causes a compile-time error.
  • 索引子存取。An indexer access. 每個索引子存取都有相關聯的類型,也就是索引子的元素類型。Every indexer access has an associated type, namely the element type of the indexer. 此外,索引子存取有相關聯的實例運算式和相關聯的引數清單。Furthermore, an indexer access has an associated instance expression and an associated argument list. 在叫 get set 用索引子存取 (或區塊) 的存取子時,評估實例運算式的結果會變成 this (此存取) 所表示的實例,而評估引數清單的結果會變成叫用的參數清單。When an accessor (the get or set block) of an indexer access is invoked, the result of evaluating the instance expression becomes the instance represented by this (This access), and the result of evaluating the argument list becomes the parameter list of the invocation.
  • 不做任何動作。Nothing. 當運算式是傳回型別為的方法調用時,就會發生這種情況 voidThis occurs when the expression is an invocation of a method with a return type of void. 分類為 nothing 的運算式只在 statement_expression (運算式語句) 的內容中有效。An expression classified as nothing is only valid in the context of a statement_expression (Expression statements).

運算式的最終結果絕對不是命名空間、類型、方法群組或事件存取。The final result of an expression is never a namespace, type, method group, or event access. 相反地,這些運算式類別是只允許在特定內容中使用的中繼結構。Rather, as noted above, these categories of expressions are intermediate constructs that are only permitted in certain contexts.

屬性存取或索引子存取一律會藉由執行 get 存取 子或 set 存取 子的調用,重新分類為值。A property access or indexer access is always reclassified as a value by performing an invocation of the get accessor or the set accessor. 特定存取子是由屬性或索引子存取的內容所決定:如果存取是指派的目標,則會叫用 set 存取 子,以指派新值給 (簡單指派) 。The particular accessor is determined by the context of the property or indexer access: If the access is the target of an assignment, the set accessor is invoked to assign a new value (Simple assignment). 否則,會叫用 get 存取 子,以取得目前值 () 的運算式值Otherwise, the get accessor is invoked to obtain the current value (Values of expressions).

運算式的值Values of expressions

大部分牽涉到運算式的結構最後都需要運算式來表示 Most of the constructs that involve an expression ultimately require the expression to denote a value. 在這種情況下,如果實際的運算式表示命名空間、類型、方法群組或 nothing,就會發生編譯時期錯誤。In such cases, if the actual expression denotes a namespace, a type, a method group, or nothing, a compile-time error occurs. 但是,如果運算式表示屬性存取、索引子存取或變數,則會隱含地替代屬性、索引子或變數的值:However, if the expression denotes a property access, an indexer access, or a variable, the value of the property, indexer, or variable is implicitly substituted:

  • 變數的值只是目前儲存在變數所識別之儲存位置的值。The value of a variable is simply the value currently stored in the storage location identified by the variable. 變數必須被視為明確指派 (明確 指派) ,才能取得其值,否則就會發生編譯時期錯誤。A variable must be considered definitely assigned (Definite assignment) before its value can be obtained, or otherwise a compile-time error occurs.
  • 藉由叫用屬性的 get 存取 子,即可取得屬性存取運算式的值。The value of a property access expression is obtained by invoking the get accessor of the property. 如果屬性沒有 get 存取 子,就會發生編譯階段錯誤。If the property has no get accessor, a compile-time error occurs. 否則,函式成員調用 (動態多載解析) 的編譯時間檢查 ,而且叫用的結果會成為屬性存取運算式的值。Otherwise, a function member invocation (Compile-time checking of dynamic overload resolution) is performed, and the result of the invocation becomes the value of the property access expression.
  • 索引子存取運算式的值是透過叫用索引子的 get 存取 子取得。The value of an indexer access expression is obtained by invoking the get accessor of the indexer. 如果索引子沒有 get 存取 子,就會發生編譯時期錯誤。If the indexer has no get accessor, a compile-time error occurs. 否則,會使用與索引子存取運算式相關聯的引數清單來執行動態多載解析) 的函數成員調用 (編譯時間檢查 ,而且叫用的結果會成為索引子存取運算式的值。Otherwise, a function member invocation (Compile-time checking of dynamic overload resolution) is performed with the argument list associated with the indexer access expression, and the result of the invocation becomes the value of the indexer access expression.

靜態和動態繫結Static and Dynamic Binding

根據組成運算式的類型或值 (引數、運算元、接收者) 來判斷作業意義的程式通常稱為系 結。The process of determining the meaning of an operation based on the type or value of constituent expressions (arguments, operands, receivers) is often referred to as binding. 例如,方法呼叫的意義取決於接收者和引數的型別。For instance the meaning of a method call is determined based on the type of the receiver and arguments. 運算子的意義取決於其運算元的型別。The meaning of an operator is determined based on the type of its operands.

在 c # 中,通常會根據其組成運算式的編譯時間類型,在編譯時期決定作業的意義。In C# the meaning of an operation is usually determined at compile-time, based on the compile-time type of its constituent expressions. 同樣地,如果運算式包含錯誤,則編譯器會偵測到錯誤並回報錯誤。Likewise, if an expression contains an error, the error is detected and reported by the compiler. 這種方法稱為 靜態 系結。This approach is known as static binding.

但是,如果運算式是動態運算式 (也就是具有類型 dynamic) 這表示它所參與的任何系結都應該以其執行時間類型為基礎 (亦即,它在運行) 時間所代表之物件的實際型別,而不是在編譯時期的型別。However, if an expression is a dynamic expression (i.e. has the type dynamic) this indicates that any binding that it participates in should be based on its run-time type (i.e. the actual type of the object it denotes at run-time) rather than the type it has at compile-time. 因此,這類作業的系結會延後,直到執行程式時執行作業的時間為止。The binding of such an operation is therefore deferred until the time where the operation is to be executed during the running of the program. 這稱為 動態繫結This is referred to as dynamic binding.

動態繫結作業時,編譯器不會執行任何檢查。When an operation is dynamically bound, little or no checking is performed by the compiler. 相反地,如果執行時間系結失敗,則會在執行時間將錯誤報表為例外狀況。Instead if the run-time binding fails, errors are reported as exceptions at run-time.

C # 中的下列作業受限於系結:The following operations in C# are subject to binding:

  • 成員存取: e.MMember access: e.M
  • 方法調用: e.M(e1, ..., eN)Method invocation: e.M(e1, ..., eN)
  • 委派調用:e(e1, ..., eN)Delegate invocation:e(e1, ..., eN)
  • 元素存取: e[e1, ..., eN]Element access: e[e1, ..., eN]
  • 物件建立: new C(e1, ..., eN)Object creation: new C(e1, ..., eN)
  • 多載一元運算子: +-!~ 、、、 ++ -- truefalseOverloaded unary operators: +, -, !, ~, ++, --, true, false
  • 多載的二元運算子: +-*/%&&& 、、 | ||?? ^ << >> == != > < >= 、、、、、、、、、、、、、、、、、 <=Overloaded binary operators: +, -, *, /, %, &, &&, |, ||, ??, ^, <<, >>, ==,!=, >, <, >=, <=
  • 指派運算子: =+=-=*=/=%=&=|=^=<<=>>=Assignment operators: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
  • 隱含和明確轉換Implicit and explicit conversions

如果沒有涉及任何動態運算式,c # 會預設為靜態系結,這表示選取進程中會使用構成運算式的編譯時間類型。When no dynamic expressions are involved, C# defaults to static binding, which means that the compile-time types of constituent expressions are used in the selection process. 不過,當上列作業中的其中一個組成運算式為動態運算式時,作業會改為動態繫結。However, when one of the constituent expressions in the operations listed above is a dynamic expression, the operation is instead dynamically bound.

系結時間Binding-time

靜態系結會在編譯時期發生,而動態繫結則會在執行時間進行。Static binding takes place at compile-time, whereas dynamic binding takes place at run-time. 在下列各節中,系結時間指的是編譯時期或執行時間,視系結的發生時間而 In the following sections, the term binding-time refers to either compile-time or run-time, depending on when the binding takes place.

下列範例說明靜態和動態系結以及系結時間的概念:The following example illustrates the notions of static and dynamic binding and of binding-time:

object  o = 5;
dynamic d = 5;

Console.WriteLine(5);  // static  binding to Console.WriteLine(int)
Console.WriteLine(o);  // static  binding to Console.WriteLine(object)
Console.WriteLine(d);  // dynamic binding to Console.WriteLine(int)

前兩個呼叫會靜態系結: Console.WriteLine 根據其引數的編譯時間類型挑選的多載。The first two calls are statically bound: the overload of Console.WriteLine is picked based on the compile-time type of their argument. 因此,系結時間是編譯時期。Thus, the binding-time is compile-time.

第三個呼叫是動態系結的: Console.WriteLine 根據其引數的執行時間類型挑選的多載。The third call is dynamically bound: the overload of Console.WriteLine is picked based on the run-time type of its argument. 發生這種情況是因為引數是動態運算式--其編譯時間類型為 dynamicThis happens because the argument is a dynamic expression -- its compile-time type is dynamic. 因此,第三個呼叫的系結時間是執行時間。Thus, the binding-time for the third call is run-time.

動態繫結Dynamic binding

動態繫結的目的是要讓 c # 程式與 動態物件 互動,也就是不遵循 c # 型別系統標準規則的物件。The purpose of dynamic binding is to allow C# programs to interact with dynamic objects, i.e. objects that do not follow the normal rules of the C# type system. 動態物件可以是其他具有不同類型系統之程式設計語言的物件,也可能是以程式設計方式設定的物件,以針對不同的作業來執行自己的系結語法。Dynamic objects may be objects from other programming languages with different types systems, or they may be objects that are programmatically setup to implement their own binding semantics for different operations.

動態物件實作為其本身語義的機制是定義的。The mechanism by which a dynamic object implements its own semantics is implementation defined. 指定的介面--重新執行的已定義--是由動態物件所執行,以通知 c # 執行時間它們具有特殊的語法。A given interface -- again implementation defined -- is implemented by dynamic objects to signal to the C# run-time that they have special semantics. 因此,每當動態物件上的作業動態繫結時,其本身的系結語義(而非本檔中所指定的 c #)都會接管。Thus, whenever operations on a dynamic object are dynamically bound, their own binding semantics, rather than those of C# as specified in this document, take over.

動態繫結的目的是要允許與動態物件的交互操作,但 c # 允許所有物件上的動態系結,不論它們是否為動態。While the purpose of dynamic binding is to allow interoperation with dynamic objects, C# allows dynamic binding on all objects, whether they are dynamic or not. 這可讓您更順暢地整合動態物件,因為這些物件的作業結果可能本身不是動態物件,但在編譯時期仍是程式設計師未知的類型。This allows for a smoother integration of dynamic objects, as the results of operations on them may not themselves be dynamic objects, but are still of a type unknown to the programmer at compile-time. 此外,動態繫結也可協助消除錯誤的反映型程式碼,即使沒有任何相關的物件是動態物件也一樣。Also dynamic binding can help eliminate error-prone reflection-based code even when no objects involved are dynamic objects.

下列各節會在套用動態繫結時,完全描述語言中的每個結構、編譯時間檢查(如果有的話),以及編譯時間結果和運算式分類。The following sections describe for each construct in the language exactly when dynamic binding is applied, what compile time checking -- if any -- is applied, and what the compile-time result and expression classification is.

組成運算式的類型Types of constituent expressions

靜態系結作業時,組成運算式的型別 (例如接收者、引數、索引或運算元) 一律視為該運算式的編譯時間型別。When an operation is statically bound, the type of a constituent expression (e.g. a receiver, an argument, an index or an operand) is always considered to be the compile-time type of that expression.

動態系結作業時,會根據組成運算式的編譯時間類型,以不同的方式決定組成運算式的類型:When an operation is dynamically bound, the type of a constituent expression is determined in different ways depending on the compile-time type of the constituent expression:

  • 編譯時間類型的組成運算式會被 dynamic 視為具有運算式在執行時間評估為的實際數值型別。A constituent expression of compile-time type dynamic is considered to have the type of the actual value that the expression evaluates to at runtime
  • 編譯時間類型為型別參數的組成運算式,會被視為具有類型參數在執行時間系結的型別。A constituent expression whose compile-time type is a type parameter is considered to have the type which the type parameter is bound to at runtime
  • 否則,會將組成運算式視為具有其編譯時間類型。Otherwise the constituent expression is considered to have its compile-time type.

運算子Operators

運算式是從 運算元 _ 和 _運算子*_ 所構成。Expressions are constructed from operands _ and _operators*_. 運算式的運算子會指出要將哪些運算套用到運算元。The operators of an expression indicate which operations to apply to the operands. 運算子範例包括 +-_/newExamples of operators include +, -, _, /, and new. 運算元範例包括常值、欄位、區域變數及運算式。Examples of operands include literals, fields, local variables, and expressions.

有三種運算子:There are three kinds of operators:

  • 一元運算子。Unary operators. 一元運算子採用一個運算元,並使用前置標記法 (例如 --x) 或後置標記法 (例如 x++) 。The unary operators take one operand and use either prefix notation (such as --x) or postfix notation (such as x++).
  • 二元運算子。Binary operators. 二元運算子採用兩個運算元,且全都使用中置標記法 (例如 x + y) 。The binary operators take two operands and all use infix notation (such as x + y).
  • 三元運算子。Ternary operator. 只有一個三元運算子( ?: )存在; 它會採用三個運算元,並使用中綴標記法 (c ? x : y) 。Only one ternary operator, ?:, exists; it takes three operands and uses infix notation (c ? x : y).

運算式中運算子的評估順序是由運算子的 *優先順序 _ 和 _ 關聯 性 * (運算子優先順序和關聯 性) 所決定。The order of evaluation of operators in an expression is determined by the precedence _ and _ associativity of the operators (Operator precedence and associativity).

運算式中的運算元會由左至右評估。Operands in an expression are evaluated from left to right. 例如,在中, F(i) + G(i++) * H(i) 方法 F 是使用舊的值來呼叫 i ,然後使用 G 的舊值來呼叫方法, i 最後, H 會使用的新值來呼叫方法 iFor example, in F(i) + G(i++) * H(i), method F is called using the old value of i, then method G is called with the old value of i, and, finally, method H is called with the new value of i. 這與運算子優先順序不同且不相關。This is separate from and unrelated to operator precedence.

部分運算子可以 多載Certain operators can be overloaded. 運算子多載可讓您針對一或兩個運算元都是使用者定義的類別或結構類型 (運算子 多載) 的作業指定使用者定義的運算子。Operator overloading permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type (Operator overloading).

運算子優先順序和關聯性Operator precedence and associativity

當運算式包含多個運算子時,運算子的 *優先順序 _ 會控制個別運算子的評估順序。When an expression contains multiple operators, the *precedence _ of the operators controls the order in which the individual operators are evaluated. 例如,運算式 x + y _ z 會評估為 x + (y * z),因為 * 運算子的優先順序高於二元 + 運算子。For example, the expression x + y _ z is evaluated as x + (y * z) because the * operator has higher precedence than the binary + operator. 運算子優先順序是由其相關聯文法結果的定義所建立。The precedence of an operator is established by the definition of its associated grammar production. 例如, additive_expression 包含由或運算子分隔的一連串 multiplicative_expression + - ,因此,和運算子的優先順序會低於 + 、和 - * / % 運算子。For example, an additive_expression consists of a sequence of multiplicative_expression s separated by + or - operators, thus giving the + and - operators lower precedence than the *, /, and % operators.

下表依優先順序從最高到最低的順序匯總所有運算子:The following table summarizes all operators in order of precedence from highest to lowest:

小節Section 類別Category 運算子Operators
主要運算式Primary expressions 主要Primary x.y f(x) a[x] x++ x-- new typeof default checked unchecked delegatex.y f(x) a[x] x++ x-- new typeof default checked unchecked delegate
一元運算子Unary operators 一元 (Unary)Unary + - ! ~ ++x --x (T)x+ - ! ~ ++x --x (T)x
算術運算子Arithmetic operators 乘法Multiplicative * / %* / %
算術運算子Arithmetic operators 加法Additive + -+ -
移位運算子Shift operators ShiftShift << >><< >>
關係和類型測試運算子Relational and type-testing operators 關係和型別測試Relational and type testing < > <= >= is as< > <= >= is as
關係和類型測試運算子Relational and type-testing operators 等式Equality == !=== !=
邏輯運算子Logical operators 邏輯 ANDLogical AND &
邏輯運算子Logical operators 邏輯 XORLogical XOR ^
邏輯運算子Logical operators 邏輯 ORLogical OR |
條件邏輯運算子Conditional logical operators 條件式 ANDConditional AND &&
條件邏輯運算子Conditional logical operators 條件式 ORConditional OR ||
Null 聯合運算子The null coalescing operator Null 聯合Null coalescing ??
條件運算子Conditional operator 條件式Conditional ?:
指派運算子匿名函數運算式Assignment operators, Anonymous function expressions 指派和 lambda 運算式Assignment and lambda expression = *= /= %= += -= <<= >>= &= ^= |= =>= *= /= %= += -= <<= >>= &= ^= |= =>

當兩個優先順序相同的運算子之間有運算元時,運算子的「關聯性」會控制執行運算的順序:When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed:

  • 除了指派運算子和 null 聯合運算子之外,所有二元運算子都是 左方關聯 的,這表示作業是由左至右執行。Except for the assignment operators and the null coalescing operator, all binary operators are left-associative, meaning that operations are performed from left to right. 例如,x + y + z 會判斷值為 (x + y) + zFor example, x + y + z is evaluated as (x + y) + z.
  • 指派運算子、null 聯合運算子和條件運算子 (?:) 是 右向關聯,這表示作業是由右至左執行。The assignment operators, the null coalescing operator and the conditional operator (?:) are right-associative, meaning that operations are performed from right to left. 例如,x = y = z 會判斷值為 x = (y = z)For example, x = y = z is evaluated as x = (y = z).

您可以使用括弧來控制優先順序和關聯性。Precedence and associativity can be controlled using parentheses. 例如,x + y * z 會先將 y 乘以 z,然後再將結果加到 x,而 (x + y) * z 則會先將 xy 相加,然後再將結果乘以 zFor example, x + y * z first multiplies y by z and then adds the result to x, but (x + y) * z first adds x and y and then multiplies the result by z.

運算子多載Operator overloading

所有一元和二元運算子都具有可在任何運算式中自動使用的預先定義的實作為。All unary and binary operators have predefined implementations that are automatically available in any expression. 除了預先定義的執行之外,也可以藉由 operator 在類別和結構中包含宣告 (運算子) 來引入使用者定義的實作為。In addition to the predefined implementations, user-defined implementations can be introduced by including operator declarations in classes and structs (Operators). 使用者定義運算子的執行一律優先于預先定義的運算子實作為:只有當沒有適用的使用者定義運算子實作為時,才會考慮預先定義的運算子實作為考慮,如 一元運算子 多載解析和 二元運算子多載解析中所述。User-defined operator implementations always take precedence over predefined operator implementations: Only when no applicable user-defined operator implementations exist will the predefined operator implementations be considered, as described in Unary operator overload resolution and Binary operator overload resolution.

可多載的 一元運算子 為:The overloadable unary operators are:

+   -   !   ~   ++   --   true   false

雖然 true 和不 false 會在運算式中明確使用 (而且不會包含在運算子優先順序和關聯性) 的優先順序資料表中,但它們被視為運算子,因為它們是在數個運算式內容中叫用:布林運算式 (布林運算式) 以及條件式邏輯 運算子 (條件式邏輯運算子) 的運算式。Although true and false are not used explicitly in expressions (and therefore are not included in the precedence table in Operator precedence and associativity), they are considered operators because they are invoked in several expression contexts: boolean expressions (Boolean expressions) and expressions involving the conditional (Conditional operator), and conditional logical operators (Conditional logical operators).

可多載的 二元運算子 為:The overloadable binary operators are:

+   -   *   /   %   &   |   ^   <<   >>   ==   !=   >   <   >=   <=

只有上面列出的運算子可以多載。Only the operators listed above can be overloaded. 尤其是,無法多載成員存取、方法調用,或、、、、、、、、、、、 = && || ?? ?: => checked unchecked new typeof default asis 運算子。In particular, it is not possible to overload member access, method invocation, or the =, &&, ||, ??, ?:, =>, checked, unchecked, new, typeof, default, as, and is operators.

二元運算子多載時,對應的指派運算子 (若有) 也會隱含地多載。When a binary operator is overloaded, the corresponding assignment operator, if any, is also implicitly overloaded. 例如,運算子的多載 * 也是運算子的多載 *=For example, an overload of operator * is also an overload of operator *=. 這會在 複合指派中進一步說明。This is described further in Compound assignment. 請注意,指派運算子本身 (=) 不能多載。Note that the assignment operator itself (=) cannot be overloaded. 指派一律會將值的簡單位複本執行到變數中。An assignment always performs a simple bit-wise copy of a value into a variable.

轉換作業(例如 (T)x )會藉由提供使用者定義的轉換 (使用者定義的轉換) 來多載。Cast operations, such as (T)x, are overloaded by providing user-defined conversions (User-defined conversions).

元素存取(例如 a[x] )不會被視為可多載的運算子。Element access, such as a[x], is not considered an overloadable operator. 相反地, (索引子) 的索引子支援使用者定義的索引。Instead, user-defined indexing is supported through indexers (Indexers).

在運算式中,運算子會使用運算子標記法來參考,而在宣告中,運算子會使用函式標記法來參考。In expressions, operators are referenced using operator notation, and in declarations, operators are referenced using functional notation. 下表顯示一元和二元運算子的運算子和函數標記法之間的關聯性。The following table shows the relationship between operator and functional notations for unary and binary operators. 在第一個專案中, op 表示任何可多載的一元前置運算子。In the first entry, op denotes any overloadable unary prefix operator. 在第二個專案中, op 代表一元後置 ++-- 運算子。In the second entry, op denotes the unary postfix ++ and -- operators. 在第三個專案中, op 表示任何可多載的二元運算子。In the third entry, op denotes any overloadable binary operator.

運算子標記法Operator notation 功能標記法Functional notation
op x operator op(x)
x op operator op(x)
x op y operator op(x,y)

使用者定義運算子宣告一律需要至少一個參數必須是包含運算子宣告的類別或結構類型。User-defined operator declarations always require at least one of the parameters to be of the class or struct type that contains the operator declaration. 因此,使用者定義的運算子不可能具有與預先定義之運算子相同的簽章。Thus, it is not possible for a user-defined operator to have the same signature as a predefined operator.

使用者定義運算子聲明無法修改運算子的語法、優先順序或關聯性。User-defined operator declarations cannot modify the syntax, precedence, or associativity of an operator. 例如, / 運算子一律是二元運算子,且一律具有在 運算子優先順序和關聯性中指定的優先順序層級,而且一律為左方關聯。For example, the / operator is always a binary operator, always has the precedence level specified in Operator precedence and associativity, and is always left-associative.

雖然使用者定義的運算子有可能執行 pleases 的任何計算,但強烈建議您不要使用產生的結果,而不是以直覺方式產生的結果。While it is possible for a user-defined operator to perform any computation it pleases, implementations that produce results other than those that are intuitively expected are strongly discouraged. 例如,的執行 operator == 應該比較兩個運算元是否相等,並傳回適當的 bool 結果。For example, an implementation of operator == should compare the two operands for equality and return an appropriate bool result.

透過條件式邏輯運算子主要運算式中個別運算子的描述會指定運算子的預先定義執行,以及適用于每個運算子的任何其他規則。The descriptions of individual operators in Primary expressions through Conditional logical operators specify the predefined implementations of the operators and any additional rules that apply to each operator. 這些描述會使用詞彙 一元運算子 多載解析 _、 _二元運算子*_ 多載解析和 _ 數值提升 *,其定義可在下列各節中找到。The descriptions make use of the terms unary operator overload resolution _, _binary operator overload resolution_, and _numeric promotion**, definitions of which are found in the following sections.

一元運算子多載解析Unary operator overload resolution

或形式的作業 op x x op ,其中是可多載的 op 一元運算子,而 x 是類型的運算式,其 X 處理方式如下:An operation of the form op x or x op, where op is an overloadable unary operator, and x is an expression of type X, is processed as follows:

  • 針對作業所提供的候選使用者定義運算子集合 X operator op(x) ,是使用 候選使用者定義運算子的規則來決定。The set of candidate user-defined operators provided by X for the operation operator op(x) is determined using the rules of Candidate user-defined operators.
  • 如果候選的使用者定義運算子集合不是空的,則這會成為作業的候選運算子集合。If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. 否則,預先定義的一元實 operator op 組(包括其提升形式)會成為作業的候選運算子集合。Otherwise, the predefined unary operator op implementations, including their lifted forms, become the set of candidate operators for the operation. 指定運算子的預先定義的實 (主要運算式一元運算子) 指定于運算子的描述中。The predefined implementations of a given operator are specified in the description of the operator (Primary expressions and Unary operators).
  • 多載解析的多載解析 規則會套用 至候選運算子集合,以選取與引數清單相關的最佳運算子 (x) ,而且這個運算子會成為多載解析程式的結果。The overload resolution rules of Overload resolution are applied to the set of candidate operators to select the best operator with respect to the argument list (x), and this operator becomes the result of the overload resolution process. 如果多載解析無法選取單一最佳運算子,則會發生系結階段錯誤。If overload resolution fails to select a single best operator, a binding-time error occurs.

二元運算子多載解析Binary operator overload resolution

表單的作業 x op y (其中是可多載的 op 二元運算子) x 是類型的運算式, Xy 是類型的運算式,其 Y 處理方式如下:An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • 由和針對作業所提供的候選使用者定義運算子集合 X Y operator op(x,y)The set of candidate user-defined operators provided by X and Y for the operation operator op(x,y) is determined. 此集合是由所提供之候選運算子的聯集 X 以及提供的候選運算子所組成 Y ,每個都是使用 候選使用者定義運算子的規則來決定。The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of Candidate user-defined operators. 如果 X 和是 Y 相同的型別,或者如果 XY 是衍生自通用基底型別,則共用的候選運算子只會在合併的集合中進行。If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.
  • 如果候選的使用者定義運算子集合不是空的,則這會成為作業的候選運算子集合。If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. 否則,預先定義的二進位執行 operator op (包括其提升形式)會成為作業的候選運算子集合。Otherwise, the predefined binary operator op implementations, including their lifted forms, become the set of candidate operators for the operation. 指定運算子的預先定義的實作為透過條件式邏輯運算子) ,在運算子 (算術運算子的描述中指定。The predefined implementations of a given operator are specified in the description of the operator (Arithmetic operators through Conditional logical operators). 針對預先定義的列舉和委派運算子,唯一考慮的運算子是由列舉或委派型別所定義的運算子,也就是其中一個運算元的系結時間型別。For predefined enum and delegate operators, the only operators considered are those defined by an enum or delegate type that is the binding-time type of one of the operands.
  • 多載解析的多載解析 規則會套用 至候選運算子集合,以選取與引數清單相關的最佳運算子 (x,y) ,而且這個運算子會成為多載解析程式的結果。The overload resolution rules of Overload resolution are applied to the set of candidate operators to select the best operator with respect to the argument list (x,y), and this operator becomes the result of the overload resolution process. 如果多載解析無法選取單一最佳運算子,則會發生系結階段錯誤。If overload resolution fails to select a single best operator, a binding-time error occurs.

候選的使用者定義運算子Candidate user-defined operators

如果有型別 T 和作業 operator op(A) ,其中 op 是可多載的運算子,而且 A 是引數清單,所提供的候選使用者定義運算子 T 集會 operator op(A) 依照下列方式決定:Given a type T and an operation operator op(A), where op is an overloadable operator and A is an argument list, the set of candidate user-defined operators provided by T for operator op(A) is determined as follows:

  • 判斷類型 T0Determine the type T0. 如果 T 是可為 null 的型別, T0 則為其基礎類型,否則 T0 等於 TIf T is a nullable type, T0 is its underlying type, otherwise T0 is equal to T.
  • 針對中的所有宣告 operator op T0 和這類運算子的所有形式,如果至少有一個運算子適用 (適用 的函式成員) 與引數清單有關 A ,則候選運算子集會由中的所有這類適用運算子組成 T0For all operator op declarations in T0 and all lifted forms of such operators, if at least one operator is applicable (Applicable function member) with respect to the argument list A, then the set of candidate operators consists of all such applicable operators in T0.
  • 否則,如果 T0object ,候選運算子的集合就會是空的。Otherwise, if T0 is object, the set of candidate operators is empty.
  • 否則,所提供的候選運算子集會 T0 是的直接基類所提供的候選運算子集 T0T0 如果是型別參數,則為的有效基類 T0Otherwise, the set of candidate operators provided by T0 is the set of candidate operators provided by the direct base class of T0, or the effective base class of T0 if T0 is a type parameter.

數值升階Numeric promotions

數值提升是由自動執行預先定義之一元和二元數值運算子之運算元的某些隱含轉換所組成。Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. 數值升級並非不同的機制,而是將多載解析套用至預先定義之運算子的效果。Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. 數值升階明確地不會影響使用者定義運算子的評估,雖然可以實行使用者定義運算子來展現類似的效果。Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects.

作為數值升級的範例,請考慮二元運算子的預先定義的實值 *As an example of numeric promotion, consider the predefined implementations of the binary * operator:

int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);

當多載解析規則 (多載 解析 規則時,) 會套用至這組運算子,其效果是選取第一個運算子,其中的隱含轉換存在於運算元類型。When overload resolution rules (Overload resolution) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types. 例如,針對作業 b * s ,其中為, b bytes 為,多載 short 解析會選取 operator *(int,int) 為最佳運算子。For example, for the operation b * s, where b is a byte and s is a short, overload resolution selects operator *(int,int) as the best operator. 因此,效果是, bs 會轉換成 int ,而結果的類型是 intThus, the effect is that b and s are converted to int, and the type of the result is int. 同樣地,對於作業 i * d 而言,如果是,則為,多載 i int d double 解析會選取 operator *(double,double) 做為最佳運算子。Likewise, for the operation i * d, where i is an int and d is a double, overload resolution selects operator *(double,double) as the best operator.

一元數值提升Unary numeric promotions

針對預先定義的 +- 和一元運算子的運算元進行一元數值提升 ~Unary numeric promotion occurs for the operands of the predefined +, -, and ~ unary operators. 一元數值提升只包含 sbyte 將、、、或類型的運算元轉換 byte short ushort char 成類型 intUnary numeric promotion simply consists of converting operands of type sbyte, byte, short, ushort, or char to type int. 此外,對於一元 - 運算子,一元數值提升會將類型的運算元轉換 uint 成類型 longAdditionally, for the unary - operator, unary numeric promotion converts operands of type uint to type long.

二進位數值升級Binary numeric promotions

針對預先定義的、、、、、、、、、、、、 + - * / % & | ^ == != > < >=<= 二元運算子的運算元,會發生二進位數值升級。Binary numeric promotion occurs for the operands of the predefined +, -, *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. 二進位數值升階會隱含地將這兩個運算元轉換成一般型別,如果是非關聯式運算子,也會成為作業的結果型別。Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. 二進位數值升階包含套用下列規則,順序如下所示:Binary numeric promotion consists of applying the following rules, in the order they appear here:

  • 如果任一個運算元的類型為 decimal ,則會將另一個運算元轉換成類型 decimal ,如果另一個運算元的類型是或,就會發生系結階段錯誤 float doubleIf either operand is of type decimal, the other operand is converted to type decimal, or a binding-time error occurs if the other operand is of type float or double.
  • 否則,如果任一個運算元的類型為 double ,則會將另一個運算元轉換成類型 doubleOtherwise, if either operand is of type double, the other operand is converted to type double.
  • 否則,如果任一個運算元的類型為 float ,則會將另一個運算元轉換成類型 floatOtherwise, if either operand is of type float, the other operand is converted to type float.
  • 否則,如果任一個運算元的類型為 ulong ,則會將另一個運算元轉換成類型 ulong ,如果另一個運算元的類型為 sbyteshort 、或,則會發生系結階段錯誤 int longOtherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a binding-time error occurs if the other operand is of type sbyte, short, int, or long.
  • 否則,如果任一個運算元的類型為 long ,則會將另一個運算元轉換成類型 longOtherwise, if either operand is of type long, the other operand is converted to type long.
  • 否則,如果任一個運算元的類型為 uint ,而另一個運算元的類型為 sbyteshortint ,則這兩個運算元會轉換為類型 longOtherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
  • 否則,如果任一個運算元的類型為 uint ,則會將另一個運算元轉換成類型 uintOtherwise, if either operand is of type uint, the other operand is converted to type uint.
  • 否則,會將這兩個運算元轉換成類型 intOtherwise, both operands are converted to type int.

請注意,第一個規則不允許混 decimal 用類型與 double 和類型的任何作業 floatNote that the first rule disallows any operations that mix the decimal type with the double and float types. 規則是因為 decimal 類型和和類型之間沒有隱含轉換 double floatThe rule follows from the fact that there are no implicit conversions between the decimal type and the double and float types.

另請注意, ulong 當另一個運算元是帶正負號的整數類資料類型時,運算元不能是類型。Also note that it is not possible for an operand to be of type ulong when the other operand is of a signed integral type. 原因是沒有整數類資料類型可以代表的完整範圍以及 ulong 帶正負號的整數類資料類型。The reason is that no integral type exists that can represent the full range of ulong as well as the signed integral types.

在上述兩種情況下,cast 運算式可以用來將一個運算元明確轉換成與另一個運算元相容的型別。In both of the above cases, a cast expression can be used to explicitly convert one operand to a type that is compatible with the other operand.

在範例中In the example

decimal AddPercent(decimal x, double percent) {
    return x * (1.0 + percent / 100.0);
}

發生系結時期錯誤,因為 decimal 無法乘 doublea binding-time error occurs because a decimal cannot be multiplied by a double. 錯誤的解決方式是將第二個運算元明確轉換為,如下所示 decimalThe error is resolved by explicitly converting the second operand to decimal, as follows:

decimal AddPercent(decimal x, double percent) {
    return x * (decimal)(1.0 + percent / 100.0);
}

提起運算子Lifted operators

提升運算子 允許在不可為 null 的實值型別上操作的預先定義和使用者定義運算子,也可用於這些類型的可為 null 形式。Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. 從符合特定需求的預先定義和使用者定義運算子來建立提升運算子,如下列所述:Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:

  • 針對一元運算子For the unary operators

    +  ++  -  --  !  ~
    

    如果運算元和結果型別都不是可為 null 的實值型別,則會有運算子的形式存在。a lifted form of an operator exists if the operand and result types are both non-nullable value types. 藉由將單一修飾詞加入 ? 至運算元和結果型別,即可建立產生的形式。The lifted form is constructed by adding a single ? modifier to the operand and result types. 如果運算元為 null,則提升運算子會產生 null 值。The lifted operator produces a null value if the operand is null. 否則,提起運算子會解除封裝運算元、套用基礎運算子,然後包裝結果。Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result.

  • 二元運算子的For the binary operators

    +  -  *  /  %  &  |  ^  <<  >>
    

    如果運算元和結果型別都是不可為 null 的實值型別,則會有運算子的形式存在。a lifted form of an operator exists if the operand and result types are all non-nullable value types. 產生的形式是藉由將單一修飾詞加入 ? 至每個運算元和結果型別來構成。The lifted form is constructed by adding a single ? modifier to each operand and result type. 如果其中一或兩個運算元為 null,則會產生 null 值, (例外狀況 &| 類型的和運算子 bool? ,如 布林邏輯運算子) 中所述。The lifted operator produces a null value if one or both operands are null (an exception being the & and | operators of the bool? type, as described in Boolean logical operators). 否則,提起運算子會解除包裝運算元、套用基礎運算子,並包裝結果。Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result.

  • 針對等號比較運算子For the equality operators

    ==  !=
    

    如果運算元型別同時為不可為 null 的實值型別,且結果型別為,則會有運算子的形式存在 boola lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. 藉由將單一修飾詞加入 ? 至每個運算元類型,即可建立此形式。The lifted form is constructed by adding a single ? modifier to each operand type. 提起運算子會將兩個 null 值視為相等,而 null 值不會與任何非 null 值相等。The lifted operator considers two null values equal, and a null value unequal to any non-null value. 如果這兩個運算元都不是 null,則提升運算子會解除包裝運算元,並套用基礎運算子來產生 bool 結果。If both operands are non-null, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.

  • 針對關聯式運算子For the relational operators

    <  >  <=  >=
    

    如果運算元型別同時為不可為 null 的實值型別,且結果型別為,則會有運算子的形式存在 boola lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. 藉由將單一修飾詞加入 ? 至每個運算元類型,即可建立此形式。The lifted form is constructed by adding a single ? modifier to each operand type. false如果一個或兩個運算元都是 null,則提升運算子會產生值。The lifted operator produces the value false if one or both operands are null. 否則,提起運算子會解除包裝運算元,並套用基礎運算子來產生 bool 結果。Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.

成員查詢Member lookup

成員查閱是一種進程,會決定型別內容中的名稱意義。A member lookup is the process whereby the meaning of a name in the context of a type is determined. 成員查閱可以在評估 simple_name 的 (簡單名稱) 或運算式中 member_access (成員存取) 的過程中發生。A member lookup can occur as part of evaluating a simple_name (Simple names) or a member_access (Member access) in an expression. 如果 simple_namemember_access 是做為 invocation_expression (方法調用) 的 primary_expression 發生,則會叫用該成員。If the simple_name or member_access occurs as the primary_expression of an invocation_expression (Method invocations), the member is said to be invoked.

如果成員是方法或事件,或者如果它是常數、委派型別的欄位或屬性 (委派) 或 dynamic (動態類型) 的型別,則會將該成員視為 >invocableIf a member is a method or event, or if it is a constant, field or property of either a delegate type (Delegates) or the type dynamic (The dynamic type), then the member is said to be invocable.

成員查閱不僅會考慮成員的名稱,也會考慮成員擁有的類型參數數目,以及成員是否可存取。Member lookup considers not only the name of a member but also the number of type parameters the member has and whether the member is accessible. 基於成員查閱的目的,泛型方法和嵌套泛型型別具有其各自宣告中指定的類型參數數目,而所有其他成員都有零型別參數。For the purposes of member lookup, generic methods and nested generic types have the number of type parameters indicated in their respective declarations and all other members have zero type parameters.

 N類型中具有類型參數之名稱的成員查閱 K    T 會以下列方式處理:A member lookup of a name N with K type parameters in a type T is processed as follows:

  • 首先,會決定一組名為的可存取成員  NFirst, a set of accessible members named N is determined:
    • 如果 T 是型別參數,則集合是  N 在指定為主要條件約束或次要條件約束的每個類型中,所命名之可存取成員集合的聯集  T ,以及中所命名的可存取成員集合) ( N objectIf T is a type parameter, then the set is the union of the sets of accessible members named N in each of the types specified as a primary constraint or secondary constraint (Type parameter constraints) for T, along with the set of accessible members named N in object.
    • 否則,此集合會由中命名的所有可存取 (成員存取) 成員  N  T ,包括繼承的成員以及中所命名的可存取成員  N objectOtherwise, the set consists of all accessible (Member access) members named N in T, including inherited members and the accessible members named N in object. 如果 T 是一種結構化型別,則會藉由替代型別引數來取得成員集,如 結構化類型的成員中所述。If T is a constructed type, the set of members is obtained by substituting type arguments as described in Members of constructed types. 包含修飾詞的成員 override 將會從集合中排除。Members that include an override modifier are excluded from the set.
  • 接下來,如果 K 是零,則會移除宣告包含類型參數的所有巢狀型別。Next, if K is zero, all nested types whose declarations include type parameters are removed. 如果不 K 是零,則會移除具有不同類型參數數目的所有成員。If K is not zero, all members with a different number of type parameters are removed. 請注意,如果 K 是零,則不會移除具有型別參數的方法,因為型別推斷程式 (型別推斷) 可能可以推斷型別引數。Note that when K is zero, methods having type parameters are not removed, since the type inference process (Type inference) might be able to infer the type arguments.
  • 接下來,如果叫用 成員,則 會從集合中移除所有的非 >invocable 成員。Next, if the member is invoked, all non-invocable members are removed from the set.
  • 接著,會從集合中移除其他成員隱藏的成員。Next, members that are hidden by other members are removed from the set. 針對集合中的每個成員 S.M ,其中是宣告成員的型別, S  M 則會套用下列規則:For every member S.M in the set, where S is the type in which the member M is declared, the following rules are applied:
    • 如果 M 是常數、欄位、屬性、事件或列舉成員,則基底類型中宣告的所有成員 S 都會從集合中移除。If M is a constant, field, property, event, or enumeration member, then all members declared in a base type of S are removed from the set.
    • 如果 M 是型別宣告,則會從集合中移除基底類型中宣告的所有非型別 S ,而且具有與基底類型中宣告之相同類型參數數目的所有類型宣告, M S 都會從集合中移除。If M is a type declaration, then all non-types declared in a base type of S are removed from the set, and all type declarations with the same number of type parameters as M declared in a base type of S are removed from the set.
    • 如果 M 是方法,則基底類型中宣告的所有非方法成員 S 都會從集合中移除。If M is a method, then all non-method members declared in a base type of S are removed from the set.
  • 接下來,會從集合中移除類別成員隱藏的介面成員。Next, interface members that are hidden by class members are removed from the set. 只有當 T 是型別參數,而且 T 具有 object 非空白的有效介面集 (類型參數條件約束) 時,此步驟才會有作用。This step only has an effect if T is a type parameter and T has both an effective base class other than object and a non-empty effective interface set (Type parameter constraints). 針對集合中的每個成員 S.M ,其中 S 是宣告成員的類型 M ,如果是以外的類別宣告,則會套用下列規則 S objectFor every member S.M in the set, where S is the type in which the member M is declared, the following rules are applied if S is a class declaration other than object:
    • 如果 M 是常數、欄位、屬性、事件、列舉成員或型別宣告,則會從集合中移除在介面宣告中宣告的所有成員。If M is a constant, field, property, event, enumeration member, or type declaration, then all members declared in an interface declaration are removed from the set.
    • 如果 M 是方法,則會從集合中移除介面宣告中宣告的所有非方法成員,並且會從集合中移除所有具有相同簽章的方法 MIf M is a method, then all non-method members declared in an interface declaration are removed from the set, and all methods with the same signature as M declared in an interface declaration are removed from the set.
  • 最後,移除隱藏的成員之後,就會判斷查閱的結果:Finally, having removed hidden members, the result of the lookup is determined:
    • 如果集合是由非方法的單一成員所組成,則這個成員是查閱的結果。If the set consists of a single member that is not a method, then this member is the result of the lookup.
    • 否則,如果集合只包含方法,則此方法群組就是查閱的結果。Otherwise, if the set contains only methods, then this group of methods is the result of the lookup.
    • 否則,查閱會不明確,而且會發生系結階段錯誤。Otherwise, the lookup is ambiguous, and a binding-time error occurs.

針對類型參數和介面以外之類型中的成員查閱,以及繼承鏈中的成員查閱, (繼承鏈中的每個介面都只有零個或一個直接基底介面) ,則查閱規則的效果只是衍生的成員會隱藏具有相同名稱或簽章的基底成員。For member lookups in types other than type parameters and interfaces, and member lookups in interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effect of the lookup rules is simply that derived members hide base members with the same name or signature. 這類單一繼承查閱永遠不可混淆。Such single-inheritance lookups are never ambiguous. 介面成員存取中描述了可能會從多個繼承介面中成員查閱產生的多義性。The ambiguities that can possibly arise from member lookups in multiple-inheritance interfaces are described in Interface member access.

基底類型Base types

針對成員查閱的目的,會將類型 T 視為具有下列基底類型:For purposes of member lookup, a type T is considered to have the following base types:

  • 如果 Tobject ,則表示 T 沒有基底類型。If T is object, then T has no base type.
  • 如果 Tenum_type,的基底類型 T 為類別類型 System.EnumSystem.ValueTypeobjectIf T is an enum_type, the base types of T are the class types System.Enum, System.ValueType, and object.
  • 如果 Tstruct_type,的基底類型 T 為類別類型 System.ValueTypeobjectIf T is a struct_type, the base types of T are the class types System.ValueType and object.
  • 如果 Tclass_type,的基底類型 T 就是的基類 T ,包括類別型別 objectIf T is a class_type, the base types of T are the base classes of T, including the class type object.
  • 如果 Tinterface_type,的基底類型 T 為的基底介面 T 和類別類型 objectIf T is an interface_type, the base types of T are the base interfaces of T and the class type object.
  • 如果 Tarray_type,的基底類型 T 為類別類型 System.ArrayobjectIf T is an array_type, the base types of T are the class types System.Array and object.
  • 如果 Tdelegate_type,的基底類型 T 為類別類型 System.DelegateobjectIf T is a delegate_type, the base types of T are the class types System.Delegate and object.

函式成員Function members

函數成員是包含可執行語句的成員。Function members are members that contain executable statements. 函數成員一律為類型的成員,而且不可以是命名空間的成員。Function members are always members of types and cannot be members of namespaces. C # 會定義下列類別的函數成員:C# defines the following categories of function members:

  • 方法Methods
  • 屬性Properties
  • 事件Events
  • 索引子Indexers
  • 使用者定義的運算子User-defined operators
  • 實例的函式Instance constructors
  • 靜態建構函式Static constructors
  • 解構函式Destructors

除了無法明確叫用) 的析構函數和靜態函式 (之外,函式成員中包含的語句是透過函數成員調用來執行。Except for destructors and static constructors (which cannot be invoked explicitly), the statements contained in function members are executed through function member invocations. 撰寫函數成員調用的實際語法取決於特定的函數成員類別。The actual syntax for writing a function member invocation depends on the particular function member category.

引數清單 ( 變數清單) 函數成員調用會提供函數成員之參數的實際值或變數參考。The argument list (Argument lists) of a function member invocation provides actual values or variable references for the parameters of the function member.

泛型方法的調用可採用型別推斷來判斷要傳遞給方法的型別引數集。Invocations of generic methods may employ type inference to determine the set of type arguments to pass to the method. 此程式會在 型別推斷中描述。This process is described in Type inference.

方法的調用、索引子、運算子和實例的函式會採用多載解析來判斷要叫用的一組候選函數成員。Invocations of methods, indexers, operators and instance constructors employ overload resolution to determine which of a candidate set of function members to invoke. 此程式會在多載 解析中描述。This process is described in Overload resolution.

一旦在系結階段識別特定的函式成員(可能是透過多載解析),就會在動態多載 解析的編譯時間檢查中描述叫用函數成員的實際執行時間程式。Once a particular function member has been identified at binding-time, possibly through overload resolution, the actual run-time process of invoking the function member is described in Compile-time checking of dynamic overload resolution.

下表摘要說明在包含可明確叫用之函式成員六個類別的結構中所發生的處理。The following table summarizes the processing that takes place in constructs involving the six categories of function members that can be explicitly invoked. 在資料表中, exyvalue 表示分類為變數或值的運算式, T 表示分類為類型的運算式、 F 方法的簡單名稱,以及 P 屬性的簡單名稱。In the table, e, x, y, and value indicate expressions classified as variables or values, T indicates an expression classified as a type, F is the simple name of a method, and P is the simple name of a property.

構建Construct 範例Example 說明Description
方法引動過程Method invocation F(x,y) 套用多載解析,以選取 F 包含類別或結構中的最佳方法。Overload resolution is applied to select the best method F in the containing class or struct. 使用引數清單叫用方法 (x,y)The method is invoked with the argument list (x,y). 如果方法不是 static ,則實例運算式為 thisIf the method is not static, the instance expression is this.
T.F(x,y) 套用多載解析,以選取 F 類別或結構中的最佳方法 TOverload resolution is applied to select the best method F in the class or struct T. 如果方法不是,就會發生系結階段錯誤 staticA binding-time error occurs if the method is not static. 使用引數清單叫用方法 (x,y)The method is invoked with the argument list (x,y).
e.F(x,y) 使用多載解析來選取類別、結構或類型所提供之介面中的最佳方法 F eOverload resolution is applied to select the best method F in the class, struct, or interface given by the type of e. 如果方法為,就會發生系結階段錯誤 staticA binding-time error occurs if the method is static. 使用實例運算式 e 和引數清單叫用方法 (x,y)The method is invoked with the instance expression e and the argument list (x,y).
屬性存取Property access P get P 會叫用包含類別或結構中之屬性的存取子。The get accessor of the property P in the containing class or struct is invoked. 如果是唯讀的,則會發生編譯時期錯誤 PA compile-time error occurs if P is write-only. 如果不 Pstatic ,則實例運算式為 thisIf P is not static, the instance expression is this.
P = value set P 使用引數清單叫用包含類別或結構中之屬性的存取子 (value)The set accessor of the property P in the containing class or struct is invoked with the argument list (value). 如果是唯讀的,則會發生編譯時期錯誤 PA compile-time error occurs if P is read-only. 如果不 Pstatic ,則實例運算式為 thisIf P is not static, the instance expression is this.
T.P get P 類別或結構中叫用屬性的存取子 TThe get accessor of the property P in the class or struct T is invoked. 如果 P 不是 staticP 為唯讀,就會發生編譯時期錯誤。A compile-time error occurs if P is not static or if P is write-only.
T.P = value set P T 使用引數清單叫用類別或結構中屬性的存取子 (value)The set accessor of the property P in the class or struct T is invoked with the argument list (value). 如果 P 不是 static 或唯讀,就會發生編譯時期錯誤 PA compile-time error occurs if P is not static or if P is read-only.
e.P get P 類別、結構或型別中所指定之屬性的存取子 e 會以實例運算式叫用 eThe get accessor of the property P in the class, struct, or interface given by the type of e is invoked with the instance expression e. 如果 P 是或,則會發生系結階段錯誤 static PA binding-time error occurs if P is static or if P is write-only.
e.P = value set P e 使用實例運算式 e 和引數清單叫用之型別的類別、結構或介面中的屬性存取子 (value)The set accessor of the property P in the class, struct, or interface given by the type of e is invoked with the instance expression e and the argument list (value). 如果 P 為, static 或如果是唯讀的,則會發生系結階段錯誤 PA binding-time error occurs if P is static or if P is read-only.
事件存取Event access E += value add E 用包含類別或結構中事件的存取子。The add accessor of the event E in the containing class or struct is invoked. 如果不 E 是靜態的,則實例運算式為 thisIf E is not static, the instance expression is this.
E -= value remove E 用包含類別或結構中事件的存取子。The remove accessor of the event E in the containing class or struct is invoked. 如果不 E 是靜態的,則實例運算式為 thisIf E is not static, the instance expression is this.
T.E += value add E 用類別或結構中的事件存取子 TThe add accessor of the event E in the class or struct T is invoked. 如果不是靜態的,則會發生系結階段錯誤 EA binding-time error occurs if E is not static.
T.E -= value remove E 用類別或結構中的事件存取子 TThe remove accessor of the event E in the class or struct T is invoked. 如果不是靜態的,則會發生系結階段錯誤 EA binding-time error occurs if E is not static.
e.E += value add E 類別、結構或介面中的事件存取子 e 是使用實例運算式叫用的 eThe add accessor of the event E in the class, struct, or interface given by the type of e is invoked with the instance expression e. 如果是靜態的,則會發生系結階段錯誤 EA binding-time error occurs if E is static.
e.E -= value remove E 類別、結構或介面中的事件存取子 e 是使用實例運算式叫用的 eThe remove accessor of the event E in the class, struct, or interface given by the type of e is invoked with the instance expression e. 如果是靜態的,則會發生系結階段錯誤 EA binding-time error occurs if E is static.
索引子存取Indexer access e[x,y] 使用多載解析來選取類別、結構或類型 e 所指定之介面中的最佳索引子。Overload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. get使用實例運算式 e 和引數清單叫用索引子的存取子 (x,y)The get accessor of the indexer is invoked with the instance expression e and the argument list (x,y). 如果索引子是唯讀的,則會發生系結階段錯誤。A binding-time error occurs if the indexer is write-only.
e[x,y] = value 套用多載解析,以選取類別、結構或類型所提供之介面中的最佳索引子 eOverload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. set使用實例運算式 e 和引數清單叫用索引子的存取子 (x,y,value)The set accessor of the indexer is invoked with the instance expression e and the argument list (x,y,value). 如果索引子是唯讀的,則會發生系結階段錯誤。A binding-time error occurs if the indexer is read-only.
運算子調用Operator invocation -x 您可以套用多載解析,以在類型指定的類別或結構中選取最佳一元運算子 xOverload resolution is applied to select the best unary operator in the class or struct given by the type of x. 使用引數清單叫用選取的運算子 (x)The selected operator is invoked with the argument list (x).
x + y 會套用多載解析,以在和類型所指定的類別或結構中選取最佳的二元運算子 x yOverload resolution is applied to select the best binary operator in the classes or structs given by the types of x and y. 使用引數清單叫用選取的運算子 (x,y)The selected operator is invoked with the argument list (x,y).
實例函式呼叫Instance constructor invocation new T(x,y) 套用多載解析,以選取類別或結構中的最佳實例處理常式 TOverload resolution is applied to select the best instance constructor in the class or struct T. 使用引數清單叫用實例的函式 (x,y)The instance constructor is invoked with the argument list (x,y).

引數清單Argument lists

每個函式成員和委派調用都包含引數清單,可提供函數成員之參數的實際值或變數參考。Every function member and delegate invocation includes an argument list which provides actual values or variable references for the parameters of the function member. 指定函式成員調用之引數清單的語法,取決於函數成員類別目錄:The syntax for specifying the argument list of a function member invocation depends on the function member category:

  • 針對實例的函式、方法、索引子和委派,會將引數指定為 argument_list,如下所述。For instance constructors, methods, indexers and delegates, the arguments are specified as an argument_list, as described below. 針對索引子,當叫 set 用存取子時,引數清單會另外包含指定為指派運算子右運算元的運算式。For indexers, when invoking the set accessor, the argument list additionally includes the expression specified as the right operand of the assignment operator.
  • 若為屬性,當叫用存取子時,引數清單是空的 get ,而且會包含在叫用存取子時,指定為指派運算子右運算元的運算式 setFor properties, the argument list is empty when invoking the get accessor, and consists of the expression specified as the right operand of the assignment operator when invoking the set accessor.
  • 若為事件,引數清單是由指定為或運算子右運算元的運算式所組成 += -=For events, the argument list consists of the expression specified as the right operand of the += or -= operator.
  • 如果是使用者定義的運算子,引數清單會包含一元運算子的單一運算元或二元運算子的兩個運算元。For user-defined operators, the argument list consists of the single operand of the unary operator or the two operands of the binary operator.

屬性 (屬性 的引數) 、事件 (事件) 以及使用者定義運算子 (運算子) 一律會以值參數的形式傳遞 (值參數) 。The arguments of properties (Properties), events (Events), and user-defined operators (Operators) are always passed as value parameters (Value parameters). 索引子 (索引子) 的引數一律會 以值 ( 參數的形式傳遞至 (參數陣列) 參數陣列) 或參數陣列。The arguments of indexers (Indexers) are always passed as value parameters (Value parameters) or parameter arrays (Parameter arrays). 這些函數成員類別不支援參考和輸出參數。Reference and output parameters are not supported for these categories of function members.

實例的函式、方法、索引子或委派調用的引數會指定為 argument_listThe arguments of an instance constructor, method, indexer or delegate invocation are specified as an argument_list:

argument_list
    : argument (',' argument)*
    ;

argument
    : argument_name? argument_value
    ;

argument_name
    : identifier ':'
    ;

argument_value
    : expression
    | 'ref' variable_reference
    | 'out' variable_reference
    ;

Argument_list 是由一或多個 引數(以逗號分隔)所組成。An argument_list consists of one or more argument s, separated by commas. 每個引數都包含一個選擇性的 argument_name ,後面接著一個 argument_valueEach argument consists of an optional argument_name followed by an argument_value. 具有 argument_name引數 稱為 *具名引數 ,而 不含 argument_name 的引數 * 則是 *位置引數An argument with an argument_name is referred to as a named argument _, whereas an _argument without an argument_name is a *positional argument. 位置引數在 _argument_list * 中的具名引數後面出現錯誤。It is an error for a positional argument to appear after a named argument in an _argument_list*.

Argument_value 可以採用下列其中一種形式:The argument_value can take one of the following forms:

  • 運算式,表示引數會以值參數的形式傳遞 (值參數) 。An expression, indicating that the argument is passed as a value parameter (Value parameters).
  • 關鍵字 ref 後面接著 Variable_reference (變數參考) ,表示會將引數做為參考參數傳遞 (參考參數) 。The keyword ref followed by a variable_reference (Variable references), indicating that the argument is passed as a reference parameter (Reference parameters). 必須明確指派變數 (明確 指派) ,才能將變數作為參考參數來傳遞。A variable must be definitely assigned (Definite assignment) before it can be passed as a reference parameter. 關鍵字 out 後面接著 Variable_reference (變數參考) ,表示引數會以輸出參數的形式傳遞 (輸出參數) 。The keyword out followed by a variable_reference (Variable references), indicating that the argument is passed as an output parameter (Output parameters). 在變數作為輸出參數傳遞的函式成員調用之後,會將變數視為明確指派 (明確 指派) 。A variable is considered definitely assigned (Definite assignment) following a function member invocation in which the variable is passed as an output parameter.

對應的參數Corresponding parameters

對於引數清單中的每個引數,函式成員或委派中都必須有對應的參數。For each argument in an argument list there has to be a corresponding parameter in the function member or delegate being invoked.

下列所使用的參數清單的判斷方式如下:The parameter list used in the following is determined as follows:

  • 針對在類別中定義的虛擬方法和索引子,會從函式成員最特定的宣告或覆寫來挑選參數清單,從接收者的靜態類型開始,然後搜尋其基類。For virtual methods and indexers defined in classes, the parameter list is picked from the most specific declaration or override of the function member, starting with the static type of the receiver, and searching through its base classes.
  • 針對介面方法和索引子,會挑選參數清單,形成最特定的成員定義,從介面類別型開始,然後搜尋基底介面。For interface methods and indexers, the parameter list is picked form the most specific definition of the member, starting with the interface type and searching through the base interfaces. 如果找不到唯一的參數清單,則會建立具有無法存取名稱且沒有選擇性參數的參數清單,讓調用無法使用具名引數或省略選擇性引數。If no unique parameter list is found, a parameter list with inaccessible names and no optional parameters is constructed, so that invocations cannot use named parameters or omit optional arguments.
  • 若為部分方法,則會使用定義部分方法宣告的參數清單。For partial methods, the parameter list of the defining partial method declaration is used.
  • 對於所有其他的函式成員和委派,只有單一參數清單,也就是使用的參數清單。For all other function members and delegates there is only a single parameter list, which is the one used.

引數或參數的位置定義為引數清單或參數清單中前面的引數或參數數目。The position of an argument or parameter is defined as the number of arguments or parameters preceding it in the argument list or parameter list.

函數成員引數的對應參數會依照下列方式建立:The corresponding parameters for function member arguments are established as follows:

  • Argument_list 實例的函式、方法、索引子和委派的引數:Arguments in the argument_list of instance constructors, methods, indexers and delegates:
    • 位置引數,在參數清單中相同位置的固定參數會與該參數相對應。A positional argument where a fixed parameter occurs at the same position in the parameter list corresponds to that parameter.
    • 函式成員的位置引數,其以其正常形式叫用的參數陣列,會對應至參數陣列,此參數陣列必須在參數清單中的相同位置發生。A positional argument of a function member with a parameter array invoked in its normal form corresponds to the parameter array, which must occur at the same position in the parameter list.
    • 函式成員的位置引數,該函式成員具有展開形式的參數陣列,而在參數清單中的相同位置沒有任何固定參數,則對應至參數陣列中的元素。A positional argument of a function member with a parameter array invoked in its expanded form, where no fixed parameter occurs at the same position in the parameter list, corresponds to an element in the parameter array.
    • 具名引數會對應到參數清單中相同名稱的參數。A named argument corresponds to the parameter of the same name in the parameter list.
    • 針對索引子, set 叫用存取子時,指定為指派運算子右運算元的運算式會對應到存取子宣告的隱含 value 參數 setFor indexers, when invoking the set accessor, the expression specified as the right operand of the assignment operator corresponds to the implicit value parameter of the set accessor declaration.
  • 針對屬性,當叫用存取子時,沒有 get 引數。For properties, when invoking the get accessor there are no arguments. set叫用存取子時,指定為指派運算子右運算元的運算式會對應到存取子宣告的隱含 value 參數 setWhen invoking the set accessor, the expression specified as the right operand of the assignment operator corresponds to the implicit value parameter of the set accessor declaration.
  • 如果使用者定義的一元運算子 (包括轉換) ,則單一運算元會對應到運算子宣告的單一參數。For user-defined unary operators (including conversions), the single operand corresponds to the single parameter of the operator declaration.
  • 如果是使用者定義的二元運算子,左運算元會對應到第一個參數,右邊的運算元則對應到運算子宣告的第二個參數。For user-defined binary operators, the left operand corresponds to the first parameter, and the right operand corresponds to the second parameter of the operator declaration.

引數清單的執行時間評估Run-time evaluation of argument lists

在執行時間處理函數成員調用時 (動態多載 解析) 的編譯時間檢查 ,引數清單的運算式或變數參考會依序從左至右評估,如下所示:During the run-time processing of a function member invocation (Compile-time checking of dynamic overload resolution), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows:

  • 如果是值參數,則會評估引數運算式,並對對應的參數類型執行隱含轉換 (隱含 轉換) 。For a value parameter, the argument expression is evaluated and an implicit conversion (Implicit conversions) to the corresponding parameter type is performed. 產生的值會成為函數成員調用中 value 參數的初始值。The resulting value becomes the initial value of the value parameter in the function member invocation.
  • 若為參考或輸出參數,則會評估變數參考,而產生的儲存位置會變成函式成員調用中參數所代表的儲存位置。For a reference or output parameter, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. 如果指定為參考或輸出參數的變數參考是 reference_type 的陣列元素,則會執行執行時間檢查,以確保陣列的元素類型與參數的類型相同。If the variable reference given as a reference or output parameter is an array element of a reference_type, a run-time check is performed to ensure that the element type of the array is identical to the type of the parameter. 如果這項檢查失敗, System.ArrayTypeMismatchException 就會擲回。If this check fails, a System.ArrayTypeMismatchException is thrown.

方法、索引子和實例的函式可能會將最右邊的參數宣告為參數陣列, (參數 陣列) 。Methods, indexers, and instance constructors may declare their right-most parameter to be a parameter array (Parameter arrays). 這類函式成員的叫用方式是以其正常形式或其展開形式來叫用 (適用的函式 成員) :Such function members are invoked either in their normal form or in their expanded form depending on which is applicable (Applicable function member):

  • 以一般形式叫用具有參數陣列的函式成員時,為參數陣列提供的引數必須是可隱含轉換的單一運算式, (隱含轉換) 至參數陣列類型。When a function member with a parameter array is invoked in its normal form, the argument given for the parameter array must be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. 在此情況下,參數陣列的運作方式與值參數完全相同。In this case, the parameter array acts precisely like a value parameter.
  • 當以參數陣列的擴充形式叫用具有參數陣列的函式成員時,調用必須為參數陣列指定零個或更多位置引數,其中每個引數都是可隱含轉換的運算式, (隱含轉換) 為參數陣列的元素類型。When a function member with a parameter array is invoked in its expanded form, the invocation must specify zero or more positional arguments for the parameter array, where each argument is an expression that is implicitly convertible (Implicit conversions) to the element type of the parameter array. 在此情況下,叫用會使用與引數數目對應的長度來建立參數陣列類型的實例,並使用指定的引數值初始化陣列實例的專案,並使用新建立的陣列實例作為實際的引數。In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, initializes the elements of the array instance with the given argument values, and uses the newly created array instance as the actual argument.

引數清單的運算式一律會依照寫入的順序進行評估。The expressions of an argument list are always evaluated in the order they are written. 因此,範例Thus, the example

class Test
{
    static void F(int x, int y = -1, int z = -2) {
        System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z);
    }

    static void Main() {
        int i = 0;
        F(i++, i++, i++);
        F(z: i++, x: i++);
    }
}

產生下列輸出produces the output

x = 0, y = 1, z = 2
x = 4, y = -1, z = 3

陣列共變數規則 (陣列共 變數) 允許陣列類型的值 A[] 為數組類型實例的參考 B[] ,但前提是已有隱含的參考轉換 B AThe array co-variance rules (Array covariance) permit a value of an array type A[] to be a reference to an instance of an array type B[], provided an implicit reference conversion exists from B to A. 因為這些規則,當 reference_type 的陣列元素作為參考或輸出參數傳遞時,需要執行時間檢查,以確保陣列的實際專案類型與參數的實際元素類型相同。Because of these rules, when an array element of a reference_type is passed as a reference or output parameter, a run-time check is required to ensure that the actual element type of the array is identical to that of the parameter. 在範例中In the example

class Test
{
    static void F(ref object x) {...}

    static void Main() {
        object[] a = new object[10];
        object[] b = new string[10];
        F(ref a[0]);        // Ok
        F(ref b[1]);        // ArrayTypeMismatchException
    }
}

的第二個調用會導致擲回, F System.ArrayTypeMismatchException 因為的實際元素類型 bstring ,而不是 objectthe second invocation of F causes a System.ArrayTypeMismatchException to be thrown because the actual element type of b is string and not object.

在展開的表單中叫用具有參數陣列的函式成員時,會完全依照陣列建立運算式(具有陣列初始化運算式的陣列建立運算式)來處理叫用 (陣列 建立運算式,) 在展開的參數周圍插入。When a function member with a parameter array is invoked in its expanded form, the invocation is processed exactly as if an array creation expression with an array initializer (Array creation expressions) was inserted around the expanded parameters. 例如,假設有宣告For example, given the declaration

void F(int x, int y, params object[] args);

下列擴充形式的方法調用:the following invocations of the expanded form of the method

F(10, 20);
F(10, 20, 30, 40);
F(10, 20, 1, "hello", 3.0);

完全對應至correspond exactly to

F(10, 20, new object[] {});
F(10, 20, new object[] {30, 40});
F(10, 20, new object[] {1, "hello", 3.0});

請特別注意,當針對參數陣列提供零個引數時,就會建立空的陣列。In particular, note that an empty array is created when there are zero arguments given for the parameter array.

使用對應的選擇性參數從函式成員省略引數時,會隱含地傳遞函數成員宣告的預設引數。When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. 因為這些一律是常數,所以其評估不會影響其餘引數的評估順序。Because these are always constant, their evaluation will not impact the evaluation order of the remaining arguments.

型別推斷Type inference

當呼叫泛型方法但未指定型別引數時, 型別推斷 進程會嘗試推斷呼叫的型別引數。When a generic method is called without specifying type arguments, a type inference process attempts to infer type arguments for the call. 型別推斷的存在,可讓您更方便地使用語法來呼叫泛型方法,並可讓程式設計人員避免指定多餘的型別資訊。The presence of type inference allows a more convenient syntax to be used for calling a generic method, and allows the programmer to avoid specifying redundant type information. 例如,假設有方法宣告:For example, given the method declaration:

class Chooser
{
    static Random rand = new Random();

    public static T Choose<T>(T first, T second) {
        return (rand.Next(2) == 0)? first: second;
    }
}

您可以叫 Choose 用方法,而不需要明確指定型別引數:it is possible to invoke the Choose method without explicitly specifying a type argument:

int i = Chooser.Choose(5, 213);                 // Calls Choose<int>

string s = Chooser.Choose("foo", "bar");        // Calls Choose<string>

透過型別推斷,型別引數 int string 是由方法的引數所決定。Through type inference, the type arguments int and string are determined from the arguments to the method.

型別推斷是做為方法調用之系結時間處理的一部分, (方法 調用) ,並在叫用的多載解析步驟之前進行。Type inference occurs as part of the binding-time processing of a method invocation (Method invocations) and takes place before the overload resolution step of the invocation. 在方法調用中指定特定方法群組,且未指定任何類型引數做為方法叫用的一部分時,會將型別推斷套用至方法群組中的每個泛型方法。When a particular method group is specified in a method invocation, and no type arguments are specified as part of the method invocation, type inference is applied to each generic method in the method group. 如果型別推斷成功,則會使用推斷的型別引數來決定後續多載解析的引數類型。If type inference succeeds, then the inferred type arguments are used to determine the types of arguments for subsequent overload resolution. 如果多載解析選擇泛型方法作為要叫用的方法,則會使用推斷的型別引數做為叫用的實際型別引數。If overload resolution chooses a generic method as the one to invoke, then the inferred type arguments are used as the actual type arguments for the invocation. 如果特定方法的型別推斷失敗,則該方法不會參與多載解析。If type inference for a particular method fails, that method does not participate in overload resolution. 型別推斷和本身的失敗不會造成系結階段錯誤。The failure of type inference, in and of itself, does not cause a binding-time error. 不過,當多載解析接著找不到任何適用的方法時,通常會導致發生系結階段錯誤。However, it often leads to a binding-time error when overload resolution then fails to find any applicable methods.

如果提供的引數數目與方法中的參數數目不同,則推斷會立即失敗。If the supplied number of arguments is different than the number of parameters in the method, then inference immediately fails. 否則,假設泛型方法具有下列簽章:Otherwise, assume that the generic method has the following signature:

Tr M<X1,...,Xn>(T1 x1, ..., Tm xm)

利用型別推斷的方法呼叫, M(E1...Em) 就是針對每個型別參數尋找唯一的型別引數, S1...Sn X1...Xn 讓呼叫 M<S1...Sn>(E1...Em) 變成有效。With a method call of the form M(E1...Em) the task of type inference is to find unique type arguments S1...Sn for each of the type parameters X1...Xn so that the call M<S1...Sn>(E1...Em) becomes valid.

在推斷過程中,每個類型參數 Xi固定 為特定類型, Si 或未使用相關聯的 界限 集進行固定。During the process of inference each type parameter Xi is either fixed to a particular type Si or unfixed with an associated set of bounds. 每個範圍都是一種類型 TEach of the bounds is some type T. 一開始,每個類型變數 Xi 都會使用一組空的界限來固定。Initially each type variable Xi is unfixed with an empty set of bounds.

型別推斷會以階段進行。Type inference takes place in phases. 每個階段都會根據上一個階段的結果,嘗試推斷更多類型變數的型別引數。Each phase will try to infer type arguments for more type variables based on the findings of the previous phase. 第一個階段會進行界限的一些初始推斷,而第二個階段則會將類型變數修正為特定類型,並推斷進一步的界限。The first phase makes some initial inferences of bounds, whereas the second phase fixes type variables to specific types and infers further bounds. 第二個階段可能必須重複數次。The second phase may have to be repeated a number of times.

注意: 只有在呼叫泛型方法時,才會發生型別推斷。Note: Type inference takes place not only when a generic method is called. 方法群組轉換的型別推斷會在 轉換方法群組的型別推斷 中描述,而尋找一組運算式的最佳一般型別,則會在 尋找一組運算式的最佳一般類型中描述。Type inference for conversion of method groups is described in Type inference for conversion of method groups and finding the best common type of a set of expressions is described in Finding the best common type of a set of expressions.

第一個階段The first phase

針對每個方法引數 EiFor each of the method arguments Ei:

  • 如果 Ei 是匿名函式,則 明確的參數類型推斷 (明確的參數類型推斷 ,) EiTiIf Ei is an anonymous function, an explicit parameter type inference (Explicit parameter type inferences) is made from Ei to Ti
  • 否則,如果的類型為, Ei Uxi 為值參數,則會 進行 較低的 系結推斷 U TiOtherwise, if Ei has a type U and xi is a value parameter then a lower-bound inference is made from U to Ti.
  • 否則,如果的 Ei 型別 U xiref 或參數, out 則會 進行 確切的推斷 U TiOtherwise, if Ei has a type U and xi is a ref or out parameter then an exact inference is made from U to Ti.
  • 否則,就不會對此引數進行任何推斷。Otherwise, no inference is made for this argument.

第二個階段The second phase

第二個階段會繼續進行,如下所示:The second phase proceeds as follows:

  • 所有不會相依于 (相依性) 的未 固定類型變數 Xi Xj , (修正) 。All unfixed type variables Xi which do not depend on (Dependence) any Xj are fixed (Fixing).
  • 如果沒有這 類型 別變數存在,則 Xi 會針對下列所有固定類型變數進行 修正If no such type variables exist, all unfixed type variables Xi are fixed for which all of the following hold:
    • 至少有一個類型變數 Xj 相依于 XiThere is at least one type variable Xj that depends on Xi
    • Xi 具有非空白的範圍集合Xi has a non-empty set of bounds
  • 如果沒有這類型別變數存在,但仍有未 固定 的型別變數,則類型推斷會失敗。If no such type variables exist and there are still unfixed type variables, type inference fails.
  • 否則,如果沒有其他未 固定 的類型變數存在,則類型推斷會成功。Otherwise, if no further unfixed type variables exist, type inference succeeds.
  • 否則,針對所有 Ei 具有對應參數類型的引數,其中輸出類型 Ti (輸出類型) 包含未處理的類型變數 Xj ,但 輸入 類型 (輸入類型) 沒有,則會 進行輸出 型別推斷 (輸出型 Ei Ti 別推斷。Otherwise, for all arguments Ei with corresponding parameter type Ti where the output types (Output types) contain unfixed type variables Xj but the input types (Input types) do not, an output type inference (Output type inferences) is made from Ei to Ti. 接著會重複第二個階段。Then the second phase is repeated.

輸入類型Input types

如果 E 是方法群組或隱含型別匿名函式,而且 T 是委派型別或運算式樹狀結構型別,則的所有參數類型 T 都是類型為的 輸入類型 E TIf E is a method group or implicitly typed anonymous function and T is a delegate type or expression tree type then all the parameter types of T are input types of E with type T.

輸出類型Output types

如果 E 是方法群組或匿名函式,而且 T 是委派型別或運算式樹狀結構型別,則的傳回型別 T 會是型 別的輸出型E TIf E is a method group or an anonymous function and T is a delegate type or expression tree type then the return type of T is an output type of E with type T.

依賴Dependence

固定 的型別變數 Xi直接相依于 未固定的型別變數(如果針對具有類型的 Xj 輸入型別中的某些具有類型的引數 Ek Tk Xj Ek Tk ,且 Xi 發生于類型的 輸出類型 Ek Tk 中)。An unfixed type variable Xi depends directly on an unfixed type variable Xj if for some argument Ek with type Tk Xj occurs in an input type of Ek with type Tk and Xi occurs in an output type of Ek with type Tk.

Xj相依于 Xi如果 Xj 直接相依Xi 或, Xi Xk Xk Xj 則取決於。Xj depends on Xi if Xj depends directly on Xi or if Xi depends directly on Xk and Xk depends on Xj. 因此,「相依于」是可轉移的,但不是「直接相依于」的自反項。Thus "depends on" is the transitive but not reflexive closure of "depends directly on".

輸出類型推斷Output type inferences

輸出型別推斷 會以下列方式 運算式建立 E 型別 TAn output type inference is made from an expression E to a type T in the following way:

  • 如果 E 是具有推斷傳回型別的匿名函式 (推斷的傳回型別 U) 而且 T 是具有傳回型別的委派型別或運算式樹狀結構型別,則會向進行下限推斷 Tb () 下限推斷 U TbIf E is an anonymous function with inferred return type U (Inferred return type) and T is a delegate type or expression tree type with return type Tb, then a lower-bound inference (Lower-bound inferences) is made from U to Tb.
  • 否則,如果 E 是方法群組,且 T 是具有參數類型和傳回類型的委派類型或運算式樹狀結構類型 T1...Tk Tb ,而且具有類型的多載解析會產生具有傳回類型的 E T1...Tk 單一方法 U ,則會 進行 下限推斷 U TbOtherwise, if E is a method group and T is a delegate type or expression tree type with parameter types T1...Tk and return type Tb, and overload resolution of E with the types T1...Tk yields a single method with return type U, then a lower-bound inference is made from U to Tb.
  • 否則,如果 E 是具有類型的運算式 U ,則會 進行 較低的 系結推斷 U TOtherwise, if E is an expression with type U, then a lower-bound inference is made from U to T.
  • 否則,就不會進行推斷。Otherwise, no inferences are made.

明確的參數類型推斷Explicit parameter type inferences

明確的參數類型推斷 會以下列方式 運算式建立 E 型別 TAn explicit parameter type inference is made from an expression E to a type T in the following way:

  • 如果 E 是具有參數類型的明確型別匿名函式 U1...Uk ,而且 T 是具有參數類型的委派類型或運算式樹狀結構類型, V1...Vk 則會對每個 Ui 精確推斷 (精確推斷) Ui 對應的進行 ViIf E is an explicitly typed anonymous function with parameter types U1...Uk and T is a delegate type or expression tree type with parameter types V1...Vk then for each Ui an exact inference (Exact inferences) is made from Ui to the corresponding Vi.

確切推斷Exact inferences

類型到類型的 精確推斷 如下所示 U VAn exact inference from a type U to a type V is made as follows:

  • 如果 V 是其中一個未 修復 的,則 Xi U 會新增至的確切界限集合 XiIf V is one of the unfixed Xi then U is added to the set of exact bounds for Xi.

  • 否則, V1...Vk U1...Uk 會檢查是否有下列任何一種情況,以決定是否適用:Otherwise, sets V1...Vk and U1...Uk are determined by checking if any of the following cases apply:

    • V是陣列類型 V1[...] ,而且 U 是相同次序的陣列類型 U1[...]V is an array type V1[...] and U is an array type U1[...] of the same rank
    • V 是型別 V1? ,而且 U 是型別 U1?V is the type V1? and U is the type U1?
    • V 是一種結構化型別 C<V1...Vk> ,而且 U 是一種結構化的型別 C<U1...Uk>V is a constructed type C<V1...Vk>and U is a constructed type C<U1...Uk>

    如果有任何一種情況,則會 每個案例對對應的進行 精確推斷 Ui ViIf any of these cases apply then an exact inference is made from each Ui to the corresponding Vi.

  • 否則不會進行推斷。Otherwise no inferences are made.

下限推斷Lower-bound inferences

類型到類型的 下限推斷 如下所示 U VA lower-bound inference from a type U to a type V is made as follows:

  • 如果 V 是其中一個未 修復 的,則 Xi U 會新增至的下限集合 XiIf V is one of the unfixed Xi then U is added to the set of lower bounds for Xi.

  • 否則,如果 V 是型別, V1? 而且是型別,則 U U1? 會從開始進行較低 U1 V1 的系結推斷。Otherwise, if V is the type V1?and U is the type U1? then a lower bound inference is made from U1 to V1.

  • 否則, U1...Uk V1...Vk 會檢查是否有下列任何一種情況,以決定是否適用:Otherwise, sets U1...Uk and V1...Vk are determined by checking if any of the following cases apply:

    • V 是陣列型別 V1[...] ,而且 U 是陣列類型 U1[...] (或有效基底型別 U1[...]) 相同次序的型別參數V is an array type V1[...] and U is an array type U1[...] (or a type parameter whose effective base type is U1[...]) of the same rank

    • V 是、或的其中一 IEnumerable<V1> ICollection<V1> IList<V1> U 維陣列類型 U1[] (或有效基底類型為的類型參數 U1[]) V is one of IEnumerable<V1>, ICollection<V1> or IList<V1> and U is a one-dimensional array type U1[](or a type parameter whose effective base type is U1[])

    • V 是一種結構化的類別、結構、介面或委派型別 C<V1...Vk> ,而且有一個唯一的型別,也就是 C<U1...Uk> U (或者,如果 U 是型別參數,則它的有效基類或其有效介面集的任何成員) 等同 (于直接或間接) ,或是直接或間接 () C<U1...Uk>V is a constructed class, struct, interface or delegate type C<V1...Vk> and there is a unique type C<U1...Uk> such that U (or, if U is a type parameter, its effective base class or any member of its effective interface set) is identical to, inherits from (directly or indirectly), or implements (directly or indirectly) C<U1...Uk>.

      (「唯一性」限制表示在案例介面中 C<T> {} class U: C<X>, C<Y> {} ,從推斷時,不會進行任何推斷, U C<T> 因為 U1 可以是 XY 。 ) (The "uniqueness" restriction means that in the case interface C<T> {} class U: C<X>, C<Y> {}, then no inference is made when inferring from U to C<T> because U1 could be X or Y.)

    如果有上述任何一種情況,則會將 每個 案例的推斷進行對應,如下所示 Ui ViIf any of these cases apply then an inference is made from each Ui to the corresponding Vi as follows:

    • 如果 Ui 不知道是參考型別,則會進行 確切的推斷If Ui is not known to be a reference type then an exact inference is made
    • 否則,如果 U 是陣列型別,則會進行 較低 系結的推斷Otherwise, if U is an array type then a lower-bound inference is made
    • 否則,如果 V 為,則 C<V1...Vk> 推斷相依于的第 i 個型別參數 COtherwise, if V is C<V1...Vk> then inference depends on the i-th type parameter of C:
      • 如果是協變數,則會進行 較低 系結的推斷。If it is covariant then a lower-bound inference is made.
      • 如果是逆變的,則會進行 上限的推斷If it is contravariant then an upper-bound inference is made.
      • 如果是不變的,則會進行 完全推斷If it is invariant then an exact inference is made.
  • 否則,就不會進行推斷。Otherwise, no inferences are made.

上限推斷Upper-bound inferences

類型到類型的 上限推斷 U V 會如下所示:An upper-bound inference from a type U to a type V is made as follows:

  • 如果 V 是其中一個未 固定 的,則 Xi U 會新增至的上限集合 XiIf V is one of the unfixed Xi then U is added to the set of upper bounds for Xi.

  • 否則, V1...Vk U1...Uk 會檢查是否有下列任何一種情況,以決定是否適用:Otherwise, sets V1...Vk and U1...Uk are determined by checking if any of the following cases apply:

    • U是陣列類型 U1[...] ,而且 V 是相同次序的陣列類型 V1[...]U is an array type U1[...] and V is an array type V1[...] of the same rank

    • UIEnumerable<Ue>ICollection<Ue> 或以及一 IList<Ue> V 維陣列類型 Ve[]U is one of IEnumerable<Ue>, ICollection<Ue> or IList<Ue> and V is a one-dimensional array type Ve[]

    • U 是型別 U1? ,而且 V 是型別 V1?U is the type U1? and V is the type V1?

    • U 是結構化類別、結構、介面或委派型別, C<U1...Uk> 而且 V 是與相同的類別、結構、介面或委派型別,繼承自 (直接或間接) ,或是直接或間接) 唯一的型別來執行 (C<V1...Vk>U is constructed class, struct, interface or delegate type C<U1...Uk> and V is a class, struct, interface or delegate type which is identical to, inherits from (directly or indirectly), or implements (directly or indirectly) a unique type C<V1...Vk>

      (「唯一性」限制表示如果有 interface C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{} ,則在從推斷至時,不會進行任何 C<U1> 推斷 V<Q>(The "uniqueness" restriction means that if we have interface C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}, then no inference is made when inferring from C<U1> to V<Q>. 推斷並非由或組成 U1 X<Q> Y<Q> 。 ) Inferences are not made from U1 to either X<Q> or Y<Q>.)

    如果有上述任何一種情況,則會將 每個 案例的推斷進行對應,如下所示 Ui ViIf any of these cases apply then an inference is made from each Ui to the corresponding Vi as follows:

    • 如果 Ui 不知道是參考型別,則會進行 確切的推斷If Ui is not known to be a reference type then an exact inference is made
    • 否則,如果 V 是陣列型別,則會進行 上限的推斷Otherwise, if V is an array type then an upper-bound inference is made
    • 否則,如果 U 為,則 C<U1...Uk> 推斷相依于的第 i 個型別參數 COtherwise, if U is C<U1...Uk> then inference depends on the i-th type parameter of C:
      • 如果是協變數,則會進行 上限的推斷If it is covariant then an upper-bound inference is made.
      • 如果是逆變的,則會進行 較低的推斷If it is contravariant then a lower-bound inference is made.
      • 如果是不變的,則會進行 完全推斷If it is invariant then an exact inference is made.
  • 否則,就不會進行推斷。Otherwise, no inferences are made.

修正Fixing

Xi 具有一組界限 的未 固定型別變數如下所示:An unfixed type variable Xi with a set of bounds is fixed as follows:

  • 一組 候選 型別會以一組 Uj 界限內的所有類型集合開始 XiThe set of candidate types Uj starts out as the set of all types in the set of bounds for Xi.
  • 接著,我們會逐一檢查每個系結,以 Xi U 找出 Xi 並非完全相同的所有型別, Uj U 從候選集合中移除。We then examine each bound for Xi in turn: For each exact bound U of Xi all types Uj which are not identical to U are removed from the candidate set. 針對沒有 U Xi 隱含轉換的所有類型下限, Uj U 會從候選集移除。For each lower bound U of Xi all types Uj to which there is not an implicit conversion from U are removed from the candidate set. 針對 U Xi 所有沒有隱含轉換之類型的上限, Uj U 都會從候選集移除。For each upper bound U of Xi all types Uj from which there is not an implicit conversion to U are removed from the candidate set.
  • 如果其餘的候選型別之間 Uj 有唯一的型別,則會 V 隱含地轉換為所有其他候選型別,然後 Xi 才會修正為 VIf among the remaining candidate types Uj there is a unique type V from which there is an implicit conversion to all the other candidate types, then Xi is fixed to V.
  • 否則,型別推斷會失敗。Otherwise, type inference fails.

推斷的傳回型別Inferred return type

匿名函式的推斷傳回型別 F 會在型別推斷和多載解析期間使用。The inferred return type of an anonymous function F is used during type inference and overload resolution. 推斷的傳回型別只能針對所有參數型別都是已知的匿名函式來決定,因為它們是明確指定的,透過匿名函式轉換提供,或在封閉式泛型方法調用的型別推斷期間推斷。The inferred return type can only be determined for an anonymous function where all parameter types are known, either because they are explicitly given, provided through an anonymous function conversion or inferred during type inference on an enclosing generic method invocation.

推斷的結果型 別會依照下列方式決定:The inferred result type is determined as follows:

  • 如果的主體 F 是具有類型的 運算式 ,則推斷的結果型別 F 是該運算式的型別。If the body of F is an expression that has a type, then the inferred result type of F is the type of that expression.
  • 如果的主體 F區塊 ,且區塊語句中的一組運算式 return 具有最佳的一般類型 T () 中 尋找一組運算式的最佳一般類型 ,則的推斷結果型別 FTIf the body of F is a block and the set of expressions in the block's return statements has a best common type T (Finding the best common type of a set of expressions), then the inferred result type of F is T.
  • 否則,就無法推斷結果型別 FOtherwise, a result type cannot be inferred for F.

推斷的傳回 別會依照下列方式決定:The inferred return type is determined as follows:

  • 如果 F 是非同步,而且的主體 F 是分類為 Nothing (運算式分類) 的運算式,或是沒有 return 語句有運算式的語句區塊,則推斷的傳回型別為 System.Threading.Tasks.TaskIf F is async and the body of F is either an expression classified as nothing (Expression classifications), or a statement block where no return statements have expressions, the inferred return type is System.Threading.Tasks.Task
  • 如果 F 是非同步,而且具有推斷的結果型別 T ,則推斷的傳回型別為 System.Threading.Tasks.Task<T>If F is async and has an inferred result type T, the inferred return type is System.Threading.Tasks.Task<T>.
  • 如果是非非同步, F 而且具有推斷的結果型別 T ,則推斷的傳回型別為 TIf F is non-async and has an inferred result type T, the inferred return type is T.
  • 否則,就無法推斷傳回型別 FOtherwise a return type cannot be inferred for F.

以匿名函式的型別推斷為例,請考慮在 Select 類別中宣告的擴充方法 System.Linq.EnumerableAs an example of type inference involving anonymous functions, consider the Select extension method declared in the System.Linq.Enumerable class:

namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TResult> Select<TSource,TResult>(
            this IEnumerable<TSource> source,
            Func<TSource,TResult> selector)
        {
            foreach (TSource element in source) yield return selector(element);
        }
    }
}

假設 System.Linq 使用子句匯入命名空間 using ,並指定 Customer 具有 Name 類型屬性的類別 string ,則 Select 可以使用方法來選取客戶清單的名稱:Assuming the System.Linq namespace was imported with a using clause, and given a class Customer with a Name property of type string, the Select method can be used to select the names of a list of customers:

List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);

擴充方法調用 (擴充方法 調用) 的 Select 處理方式是將調用重寫為靜態方法調用:The extension method invocation (Extension method invocations) of Select is processed by rewriting the invocation to a static method invocation:

IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);

由於未明確指定類型引數,因此會使用型別推斷來推斷型別引數。Since type arguments were not explicitly specified, type inference is used to infer the type arguments. 首先, customers 引數與 source 參數相關,推斷 TCustomerFirst, the customers argument is related to the source parameter, inferring T to be Customer. 然後,使用上面所述的匿名函式類型推斷 c 程式,提供類型 Customer ,而運算式與 c.Name 參數的傳回型別 selector (推斷為)相關 S stringThen, using the anonymous function type inference process described above, c is given type Customer, and the expression c.Name is related to the return type of the selector parameter, inferring S to be string. 因此,調用相當於Thus, the invocation is equivalent to

Sequence.Select<Customer,string>(customers, (Customer c) => c.Name)

而結果的型別為 IEnumerable<string>and the result is of type IEnumerable<string>.

下列範例示範匿名函式型別推斷如何讓類型資訊在泛型方法調用中的引數之間進行「流程」。The following example demonstrates how anonymous function type inference allows type information to "flow" between arguments in a generic method invocation. 提供方法:Given the method:

static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2) {
    return f2(f1(value));
}

調用的型別推斷:Type inference for the invocation:

double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds);

繼續進行,如下所示:首先,引數 "1:15:30"value 參數相關,推斷 Xstringproceeds as follows: First, the argument "1:15:30" is related to the value parameter, inferring X to be string. 然後,會為第一個匿名函式的參數 s 指定推斷型別 string ,而且運算式會與 TimeSpan.Parse(s) 的傳回型別 f1 (推斷為)相關聯 Y System.TimeSpanThen, the parameter of the first anonymous function, s, is given the inferred type string, and the expression TimeSpan.Parse(s) is related to the return type of f1, inferring Y to be System.TimeSpan. 最後,第二個匿名函式的參數 t 會獲得推斷型別 System.TimeSpan ,而且運算式與 t.TotalSeconds 的傳回型別 f2 (推斷為)相關 Z doubleFinally, the parameter of the second anonymous function, t, is given the inferred type System.TimeSpan, and the expression t.TotalSeconds is related to the return type of f2, inferring Z to be double. 因此,調用的結果是類型 doubleThus, the result of the invocation is of type double.

方法群組轉換的型別推斷Type inference for conversion of method groups

類似于泛型方法的呼叫,當 M 包含泛型方法的方法群組轉換成指定的委派類型 D (方法群組轉換) 時,也必須套用型別推斷。Similar to calls of generic methods, type inference must also be applied when a method group M containing a generic method is converted to a given delegate type D (Method group conversions). 指定方法Given a method

Tr M<X1...Xn>(T1 x1 ... Tm xm)

而且要 M 指派給委派類型的方法群組, D 推斷類型的工作就是尋找型別引數, S1...Sn 讓運算式:and the method group M being assigned to the delegate type D the task of type inference is to find type arguments S1...Sn so that the expression:

M<S1...Sn>

會變成與) 的 (委派 宣告相容 Dbecomes compatible (Delegate declarations) with D.

不同于泛型方法呼叫的型別推斷演算法,在此案例中,只有引數 類型,沒有引數 運算式Unlike the type inference algorithm for generic method calls, in this case there are only argument types, no argument expressions. 尤其是,沒有任何匿名函式,因此不需要多個推斷階段。In particular, there are no anonymous functions and hence no need for multiple phases of inference.

相反地,所有的 Xi 都會被視為未 固定 的,而 的每個引數類型到的對應參數類型都會進行 較低的 系結推斷 Uj D Tj MInstead, all Xi are considered unfixed, and a lower-bound inference is made from each argument type Uj of D to the corresponding parameter type Tj of M. 如果找不到任何 Xi 界限的,則類型推斷會失敗。If for any of the Xi no bounds were found, type inference fails. 否則,所有的 Xi 都會 修正 為對應的 Si ,這是型別推斷的結果。Otherwise, all Xi are fixed to corresponding Si, which are the result of type inference.

尋找一組運算式的最佳一般類型Finding the best common type of a set of expressions

在某些情況下,必須針對一組運算式推斷一般類型。In some cases, a common type needs to be inferred for a set of expressions. 尤其是,隱含類型陣列的專案類型,以及具有 區塊 主體之匿名函式的傳回型別,都會以這種方式找到。In particular, the element types of implicitly typed arrays and the return types of anonymous functions with block bodies are found in this way.

以直覺方式,假設有一組運算式, E1...Em 則此推斷應等同于呼叫方法Intuitively, given a set of expressions E1...Em this inference should be equivalent to calling a method

Tr M<X>(X x1 ... X xm)

with Ei 做為引數。with the Ei as arguments.

更精確地說,推斷是從未 固定 的型別變數開始 XMore precisely, the inference starts out with an unfixed type variable X. 然後,會 每個產生 輸出型別推斷 Ei XOutput type inferences are then made from each Ei to X. 最後, X固定 的,而且如果成功,則產生的型別 S 是運算式所產生的最佳一般型別。Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions. 如果不 S 存在,則運算式沒有最佳的一般類型。If no such S exists, the expressions have no best common type.

多載解析Overload resolution

多載解析是一種系結時間機制,可在指定引數清單和一組候選函數成員的情況之下,選取最佳的函式成員來叫用。Overload resolution is a binding-time mechanism for selecting the best function member to invoke given an argument list and a set of candidate function members. 多載解析會選取要在 c # 中的下列不同內容中叫用的函式成員:Overload resolution selects the function member to invoke in the following distinct contexts within C#:

上述每個內容都會以各自獨特的方式定義一組候選函式成員和引數清單,如上面所列各節中的詳細說明。Each of these contexts defines the set of candidate function members and the list of arguments in its own unique way, as described in detail in the sections listed above. 例如,方法調用的候選集合不包含標示 override (成員查閱) 的方法,而且如果衍生類別中的任何方法適用 (方法調用) ,則基類中的方法不是候選項目。For example, the set of candidates for a method invocation does not include methods marked override (Member lookup), and methods in a base class are not candidates if any method in a derived class is applicable (Method invocations).

一旦識別出候選函式成員和引數清單之後,在所有情況下,最佳函式成員的選取都會相同:Once the candidate function members and the argument list have been identified, the selection of the best function member is the same in all cases:

  • 假設有一組適用的候選函式成員,就會找到該集合中的最佳函數成員。Given the set of applicable candidate function members, the best function member in that set is located. 如果集合中只有一個函式成員,則該函數成員是最佳的函式成員。If the set contains only one function member, then that function member is the best function member. 否則,最好的函式成員就是一個函數成員,比起指定引數清單的所有其他函式成員更好,前提是每個函式成員都是使用 更好的函式成員中的規則來與所有其他函式成員進行比較。Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in Better function member. 如果沒有正好一個比所有其他函式成員更好的函式成員,則函數成員調用會不明確,而且會發生系結階段錯誤。If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a binding-time error occurs.

下列章節定義詞彙 * 適用的函式 成員 _ 和 _ *更好 的函式成員 * * 的確切意義。The following sections define the exact meanings of the terms applicable function member _ and _better function member**.

適用的函數成員Applicable function member

當下列所有條件都成立時,就會將函式成員視為與引數清單相關的 函數成員 AA function member is said to be an applicable function member with respect to an argument list A when all of the following are true:

  • 中的每個引數 A 會對應至函式成員宣告中的參數(如 對應的參數中所述),而且沒有任何引數對應的任何參數都是選擇性參數。Each argument in A corresponds to a parameter in the function member declaration as described in Corresponding parameters, and any parameter to which no argument corresponds is an optional parameter.
  • 針對中的每個引數 A ,引數的參數傳遞模式 (例如、值、 refout) 等同于對應參數的參數傳遞模式,以及For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical to the parameter passing mode of the corresponding parameter, and
    • 如果是值參數或參數陣列, (隱含 轉換) 存在於對應參數類型的引數中,或for a value parameter or a parameter array, an implicit conversion (Implicit conversions) exists from the argument to the type of the corresponding parameter, or
    • 針對 refout 參數,引數的類型與對應參數的類型相同。for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter. 畢竟, refout 參數是傳遞引數的別名。After all, a ref or out parameter is an alias for the argument passed.

針對包含參數陣列的函式成員,如果函式成員適用于上述規則,則會被視為適用于其 *標準格式 _。For a function member that includes a parameter array, if the function member is applicable by the above rules, it is said to be applicable in its *normal form _. 如果包含參數陣列的函式成員不是適用于其一般格式,則函式成員可能會改為適用于其 _ 展開的表單 *:If a function member that includes a parameter array is not applicable in its normal form, the function member may instead be applicable in its _*expanded form**:

  • 展開的表單的建立方式,是將函數成員宣告中的參數陣列取代為參數陣列元素類型的零或多個值參數,讓引數清單中的引數數目 A 符合參數的總數目。The expanded form is constructed by replacing the parameter array in the function member declaration with zero or more value parameters of the element type of the parameter array such that the number of arguments in the argument list A matches the total number of parameters. 如果 A 引數的引數數目少於函數成員宣告中的固定參數數目,則無法建立函式成員的擴充形式,因此不適用。If A has fewer arguments than the number of fixed parameters in the function member declaration, the expanded form of the function member cannot be constructed and is thus not applicable.
  • 否則,如果對 A 引數的參數傳遞模式中的每個引數,都與對應參數的參數傳遞模式相同,則會適用展開的表單,而且Otherwise, the expanded form is applicable if for each argument in A the parameter passing mode of the argument is identical to the parameter passing mode of the corresponding parameter, and
    • 若為固定值參數或展開所建立的值參數,隱含轉換 (隱含 轉換) 存在於引數的類型與對應參數的類型,或for a fixed value parameter or a value parameter created by the expansion, an implicit conversion (Implicit conversions) exists from the type of the argument to the type of the corresponding parameter, or
    • 針對 refout 參數,引數的類型與對應參數的類型相同。for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter.

更好的函式成員Better function member

為了決定更好的函式成員,移除引數清單 A 會以其在原始引數清單中出現的順序,直接包含引數運算式本身。For the purposes of determining the better function member, a stripped-down argument list A is constructed containing just the argument expressions themselves in the order they appear in the original argument list.

每個候選函數成員的參數清單會以下列方式建立:Parameter lists for each of the candidate function members are constructed in the following way:

  • 如果函數成員僅適用于展開的表單,則會使用展開的表單。The expanded form is used if the function member was applicable only in the expanded form.
  • 未對應引數的選擇性參數會從參數清單中移除Optional parameters with no corresponding arguments are removed from the parameter list
  • 這些參數會重新排序,使其出現在引數清單中與對應引數相同的位置。The parameters are reordered so that they occur at the same position as the corresponding argument in the argument list.

假設有一個引數清單 A 包含一組引數運算式 {E1, E2, ..., En} ,以及兩個適用的函式成員 MpMq 參數類型 {P1, P2, ..., Pn}{Q1, Q2, ..., Qn}Mp 則定義為比 if 更好Mq 函式成員Given an argument list A with a set of argument expressions {E1, E2, ..., En} and two applicable function members Mp and Mq with parameter types {P1, P2, ..., Pn} and {Q1, Q2, ..., Qn}, Mp is defined to be a better function member than Mq if

  • 針對每個引數,從到到的隱含轉換 Ex Qx 不優於從轉換成的隱含轉換 Ex Px ,以及for each argument, the implicit conversion from Ex to Qx is not better than the implicit conversion from Ex to Px, and
  • 若至少有一個引數,從轉換 ExPx 優於從轉換為 Ex Qxfor at least one argument, the conversion from Ex to Px is better than the conversion from Ex to Qx.

執行這項評估時,如果 MpMq 適用于其展開的表單,則為, PxQx 參考參數清單的擴充形式中的參數。When performing this evaluation, if Mp or Mq is applicable in its expanded form, then Px or Qx refers to a parameter in the expanded form of the parameter list.

如果參數類型順序  {P1, P2, ..., Pn}{Q1, Q2, ..., Qn} 相等 (也就是每個都 Pi 有對應) 的身分識別轉換 Qi ,則會依序套用下列系結規則,以判斷更好的函數成員。In case the parameter type sequences {P1, P2, ..., Pn} and {Q1, Q2, ..., Qn} are equivalent (i.e. each Pi has an identity conversion to the corresponding Qi), the following tie-breaking rules are applied, in order, to determine the better function member.

  • 如果是非 Mp 泛型方法且 Mq 為泛型方法,則 Mp 比更好 MqIf Mp is a non-generic method and Mq is a generic method, then Mp is better than Mq.
  • 否則,如果 Mp 適用于其一般形式且 Mq 具有陣列, params 且僅適用于其展開的表單,則 Mp 比更好 MqOtherwise, if Mp is applicable in its normal form and Mq has a params array and is applicable only in its expanded form, then Mp is better than Mq.
  • 否則,如果 Mp 的宣告參數多於 Mq ,則 Mp 比更好 MqOtherwise, if Mp has more declared parameters than Mq, then Mp is better than Mq. 如果這兩個方法都具有 params 陣列,且僅適用于其展開的表單,就會發生這種情況。This can occur if both methods have params arrays and are applicable only in their expanded forms.
  • 否則,如果的所有參數 Mp 都有對應的引數,而預設引數必須在中至少替代一個選擇性參數,則 Mq Mp 比更好 MqOtherwise if all parameters of Mp have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in Mq then Mp is better than Mq.
  • 否則,如果 Mp 有比更多的特定參數類型 Mq ,則 Mp 比更好 MqOtherwise, if Mp has more specific parameter types than Mq, then Mp is better than Mq. 讓和表示和的未具現化 {R1, R2, ..., Rn} {S1, S2, ..., Sn} 和未展開參數類型 Mp MqLet {R1, R2, ..., Rn} and {S1, S2, ..., Sn} represent the uninstantiated and unexpanded parameter types of Mp and Mq. Mp的參數類型比起的更為明確, Mq 如果每個參數的 Rx 特定比更不明確, Sx 而且至少要有一個參數, Rx 則更明確 Sx 地表示:Mp's parameter types are more specific than Mq's if, for each parameter, Rx is not less specific than Sx, and, for at least one parameter, Rx is more specific than Sx:
    • 類型參數比非類型參數更不特定。A type parameter is less specific than a non-type parameter.
    • 以遞迴方式來說,如果至少有一個型別引數是更特定的類型引數,而且沒有任何類型引數比另一個類型引數更明確,則結構化型別會比另一個具有相同類型) 引數的型別引數 (更明確。Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other.
    • 陣列類型比其他陣列類型更明確 (具有相同維度數目的陣列) 如果第一個的元素類型比第二個專案類型更明確。An array type is more specific than another array type (with the same number of dimensions) if the element type of the first is more specific than the element type of the second.
  • 否則,如果其中一個成員是非提起運算子,另一個成員是一個帶向的運算子,則未提升的運算子比較好。Otherwise if one member is a non-lifted operator and the other is a lifted operator, the non-lifted one is better.
  • 否則,這兩個函數成員都不是比較好的做法。Otherwise, neither function member is better.

從運算式進行更好的轉換Better conversion from expression

假設 C1 從運算式轉換成類型的隱含轉換 E T1 ,以及 C2 從運算式轉換成類型的隱含轉換, E T2 C1 則會比 C2 E 不完全相符, T2 而且至少有下列其中一個保留的轉換更好:Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if E does not exactly match T2 and at least one of the following holds:

完全相符的運算式Exactly matching Expression

假設有一個運算式 E 和一個型別 TE T 如果下列其中一項保存,就完全相符:Given an expression E and a type T, E exactly matches T if one of the following holds:

  • E 有型別 S ,而身分識別轉換存在 STE has a type S, and an identity conversion exists from S to T
  • E 是匿名函式, T 它可以是委派型別,也可以是 D 運算式樹狀架構型別, Expression<D> 以及下列其中一個保存:E is an anonymous function, T is either a delegate type D or an expression tree type Expression<D> and one of the following holds:
    • X E 在 (推斷的傳回型別) 的參數清單內容中,有一個推斷的傳回型別存在 D ,而且會從傳回型別轉換 XDAn inferred return type X exists for E in the context of the parameter list of D (Inferred return type), and an identity conversion exists from X to the return type of D
    • 可能 E 是非非同步,且 D 具有傳回型別 YE 非同步且 D 具有傳回型別 Task<Y> ,而且有下列其中一項保留:Either E is non-async and D has a return type Y or E is async and D has a return type Task<Y>, and one of the following holds:
      • 的主體 E 是完全符合的運算式 YThe body of E is an expression that exactly matches Y
      • 的主體 E 是語句區塊,其中每個 return 語句會傳回完全相符的運算式 YThe body of E is a statement block where every return statement returns an expression that exactly matches Y

更好的轉換目標Better conversion target

假設有兩種不同的類型 T1T2T1 則比起 T2 從隱含轉換成不 T2 T1 存在,以及至少下列其中一個保留的目標是較佳的轉換目標:Given two different types T1 and T2, T1 is a better conversion target than T2 if no implicit conversion from T2 to T1 exists, and at least one of the following holds:

  • 從隱含轉換 T1T2 existsAn implicit conversion from T1 to T2 exists
  • T1 是委派類型 D1 或運算式樹狀架構類型 Expression<D1>T2 是委派類型 D2 或運算式樹狀結構類型 Expression<D2>D1 具有傳回類型 S1 和下列其中一個保留:T1 is either a delegate type D1 or an expression tree type Expression<D1>, T2 is either a delegate type D2 or an expression tree type Expression<D2>, D1 has a return type S1 and one of the following holds:
    • D2 傳回 voidD2 is void returning
    • D2 具有傳回型別 S2 ,而且 S1 比起的轉換目標更好 S2D2 has a return type S2, and S1 is a better conversion target than S2
  • T1Task<S1>T2Task<S2> ,而且 S1 比起的轉換目標更好 S2T1 is Task<S1>, T2 is Task<S2>, and S1 is a better conversion target than S2
  • T1S1 或, S1? 其中 S1 是帶正負號的整數類資料類型,而 T2S2S2? S2 不帶正負號的整數類型。T1 is S1 or S1? where S1 is a signed integral type, and T2 is S2 or S2? where S2 is an unsigned integral type. 具體來說:Specifically:
    • S1sbyte S2 ,且為 byteushortuintulongS1 is sbyte and S2 is byte, ushort, uint, or ulong
    • S1short S2 ,且為 ushortuint 或。 ulongS1 is short and S2 is ushort, uint, or ulong
    • S1int S2 ,且為 uint 、或。 ulongS1 is int and S2 is uint, or ulong
    • S1long ,且 S2ulongS1 is long and S2 is ulong

泛型類別中的多載Overloading in generic classes

雖然宣告的簽章必須是唯一的,但類型引數的替代可能會產生相同的簽章。While signatures as declared must be unique, it is possible that substitution of type arguments results in identical signatures. 在這種情況下,上述多載解析的系結規則將挑選最特定的成員。In such cases, the tie-breaking rules of overload resolution above will pick the most specific member.

下列範例會根據此規則顯示有效且不正確多載:The following examples show overloads that are valid and invalid according to this rule:

interface I1<T> {...}

interface I2<T> {...}

class G1<U>
{
    int F1(U u);                  // Overload resolution for G<int>.F1
    int F1(int i);                // will pick non-generic

    void F2(I1<U> a);             // Valid overload
    void F2(I2<U> a);
}

class G2<U,V>
{
    void F3(U u, V v);            // Valid, but overload resolution for
    void F3(V v, U u);            // G2<int,int>.F3 will fail

    void F4(U u, I1<V> v);        // Valid, but overload resolution for    
    void F4(I1<V> v, U u);        // G2<I1<int>,int>.F4 will fail

    void F5(U u1, I1<V> v2);      // Valid overload
    void F5(V v1, U u2);

    void F6(ref U u);             // valid overload
    void F6(out V v);
}

動態多載解析的編譯時間檢查Compile-time checking of dynamic overload resolution

針對大部分動態系結的作業,可能會在編譯時期未知的解析候選項目集合。For most dynamically bound operations the set of possible candidates for resolution is unknown at compile-time. 但是在某些情況下,候選集在編譯時期是已知的:In certain cases, however the candidate set is known at compile-time:

  • 使用動態引數的靜態方法呼叫Static method calls with dynamic arguments
  • 接收者不是動態運算式的實例方法呼叫Instance method calls where the receiver is not a dynamic expression
  • 接收器不是動態運算式的索引子呼叫Indexer calls where the receiver is not a dynamic expression
  • 使用動態引數的函式呼叫Constructor calls with dynamic arguments

在這些情況下,會針對每個候選項目執行有限的編譯時間檢查,以查看是否有任何可能在執行時間套用。這項檢查是由下列步驟所組成:In these cases a limited compile-time check is performed for each candidate to see if any of them could possibly apply at run-time.This check consists of the following steps:

  • 部分類型推斷:不直接或間接相依于型別引數的任何類型引數, dynamic 都會使用 型別推斷的規則來推斷。Partial type inference: Any type argument that does not depend directly or indirectly on an argument of type dynamic is inferred using the rules of Type inference. 其餘的類型引數未知。The remaining type arguments are unknown.
  • 部分適用性檢查:根據適用的函式 成員檢查適用性,但忽略類型未知的參數。Partial applicability check: Applicability is checked according to Applicable function member, but ignoring parameters whose types are unknown.
  • 如果沒有候選項通過這項測試,就會發生編譯時期錯誤。If no candidate passes this test, a compile-time error occurs.

函數成員調用Function member invocation

本節說明在執行時間發生的程式,以叫用特定的函式成員。This section describes the process that takes place at run-time to invoke a particular function member. 假設系結時間進程已經決定要叫用的特定成員,可能的方法是將多載解析套用至一組候選函數成員。It is assumed that a binding-time process has already determined the particular member to invoke, possibly by applying overload resolution to a set of candidate function members.

為了描述調用程式,函式成員分為兩類:For purposes of describing the invocation process, function members are divided into two categories:

  • 靜態函數成員。Static function members. 這些是實例的函式、靜態方法、靜態屬性存取子,以及使用者定義的運算子。These are instance constructors, static methods, static property accessors, and user-defined operators. 靜態函數成員一律為非虛擬。Static function members are always non-virtual.
  • 實例函數成員。Instance function members. 這些是實例方法、實例屬性存取子和索引子存取子。These are instance methods, instance property accessors, and indexer accessors. 實例函數成員為非虛擬或虛擬的,而且一律會在特定的實例上叫用。Instance function members are either non-virtual or virtual, and are always invoked on a particular instance. 實例運算式會計算實例,而且它會在函式成員內變成可存取, this (此存取) 。The instance is computed by an instance expression, and it becomes accessible within the function member as this (This access).

函數成員調用的執行時間處理包含下列步驟,其中 M 是函數成員,而如果 M 是實例成員, E 則是實例運算式:The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:

  • 如果 M 是靜態函數成員:If M is a static function member:

    • 引數清單中會評估 引數清單中所述的引數清單。The argument list is evaluated as described in Argument lists.
    • 叫用 MM is invoked.
  • 如果 M 是在 value_type 中宣告的實例函數成員:If M is an instance function member declared in a value_type:

    • E 會進行評估。E is evaluated. 如果此評估造成例外狀況,則不會執行任何進一步的步驟。If this evaluation causes an exception, then no further steps are executed.
    • 如果未 E 分類為變數,則會建立型別的暫存區域變數, E 並將的值指派給 E 該變數。If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E 然後重新分類為該暫存區域變數的參考。E is then reclassified as a reference to that temporary local variable. 暫時變數可以像內部存取 this M ,但無法以任何其他方式存取。The temporary variable is accessible as this within M, but not in any other way. 因此,只有在 E 是真正的變數時,呼叫端才能觀察到所進行的變更 M thisThus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.
    • 引數清單中會評估 引數清單中所述的引數清單。The argument list is evaluated as described in Argument lists.
    • 叫用 MM is invoked. 所參考的變數 E 會成為所參考的 this 變數。The variable referenced by E becomes the variable referenced by this.
  • 如果 M 是在 reference_type 中宣告的實例函數成員:If M is an instance function member declared in a reference_type:

    • E 會進行評估。E is evaluated. 如果此評估造成例外狀況,則不會執行任何進一步的步驟。If this evaluation causes an exception, then no further steps are executed.
    • 引數清單中會評估 引數清單中所述的引數清單。The argument list is evaluated as described in Argument lists.
    • 如果的型別 Evalue_type,則會執行) 的裝箱轉換 (將轉換轉換 E 成型別 object ,而且 E 會被視為 object 在下列步驟中的型別。If the type of E is a value_type, a boxing conversion (Boxing conversions) is performed to convert E to type object, and E is considered to be of type object in the following steps. 在此情況下, M 只能是的成員 System.ObjectIn this case, M could only be a member of System.Object.
    • 的值 E 會被檢查為有效。The value of E is checked to be valid. 如果的值 Enull ,則會擲回, System.NullReferenceException 且不會執行任何進一步的步驟。If the value of E is null, a System.NullReferenceException is thrown and no further steps are executed.
    • 將會決定要叫用的函式成員實作為:The function member implementation to invoke is determined:
      • 如果的系結時間型別是介面,則叫用的函 E 式成員是由所 M 參考之實例的執行時間型別所提供的實作為 EIf the binding-time type of E is an interface, the function member to invoke is the implementation of M provided by the run-time type of the instance referenced by E. 這個函式成員是藉由將介面對應規則套用 (介面) 對應 來決定,以判斷由所 M 參考之實例的執行時間型別所提供的實作為 EThis function member is determined by applying the interface mapping rules (Interface mapping) to determine the implementation of M provided by the run-time type of the instance referenced by E.
      • 否則,如果是虛擬函式成員,則叫用的函 M 式成員是 M 由所參考之實例的執行時間型別所提供的實作為 EOtherwise, if M is a virtual function member, the function member to invoke is the implementation of M provided by the run-time type of the instance referenced by E. 這個函式成員是藉由套用規則來決定最 (的 虛擬方法) 的虛擬方法 M ,而這是與所參考之實例的執行時間型別有關 EThis function member is determined by applying the rules for determining the most derived implementation (Virtual methods) of M with respect to the run-time type of the instance referenced by E.
      • 否則,是非 M 虛擬函式成員,而叫用的函式成員本身就是 MOtherwise, M is a non-virtual function member, and the function member to invoke is M itself.
    • 叫用上述步驟中所決定的函式成員執行。The function member implementation determined in the step above is invoked. 所參考的物件 E 會成為所參考的 this 物件。The object referenced by E becomes the object referenced by this.

對盒裝實例的調用Invocations on boxed instances

在下列情況下,可以透過 value_type 的已封裝實例叫用在 value_type 中執行的函式成員:A function member implemented in a value_type can be invoked through a boxed instance of that value_type in the following situations:

  • 當函數成員是 override 繼承自型別的方法時 object ,會透過型別的實例運算式叫用 objectWhen the function member is an override of a method inherited from type object and is invoked through an instance expression of type object.
  • 當函式成員是介面函式成員的執行時,會透過 interface_type 的實例運算式叫用。When the function member is an implementation of an interface function member and is invoked through an instance expression of an interface_type.
  • 當函數成員透過委派叫用時。When the function member is invoked through a delegate.

在這些情況下,會將盒裝實例視為包含 value_type 的變數,而這個變數會成為函式成員調用內所參考的變數 thisIn these situations, the boxed instance is considered to contain a variable of the value_type, and this variable becomes the variable referenced by this within the function member invocation. 特別是,這表示在已封裝的實例上叫用函式成員時,函式成員可能會修改包含在已加入的實例中的值。In particular, this means that when a function member is invoked on a boxed instance, it is possible for the function member to modify the value contained in the boxed instance.

主要運算式Primary expressions

主要運算式包括最簡單的運算式形式。Primary expressions include the simplest forms of expressions.

primary_expression
    : primary_no_array_creation_expression
    | array_creation_expression
    ;

primary_no_array_creation_expression
    : literal
    | interpolated_string_expression
    | simple_name
    | parenthesized_expression
    | member_access
    | invocation_expression
    | element_access
    | this_access
    | base_access
    | post_increment_expression
    | post_decrement_expression
    | object_creation_expression
    | delegate_creation_expression
    | anonymous_object_creation_expression
    | typeof_expression
    | checked_expression
    | unchecked_expression
    | default_value_expression
    | nameof_expression
    | anonymous_method_expression
    | primary_no_array_creation_expression_unsafe
    ;

主要運算式 array_creation_expression s 和 primary_no_array_creation_expression 之間劃分。Primary expressions are divided between array_creation_expression s and primary_no_array_creation_expression s. 以這種方式來處理陣列建立運算式,而不是與其他簡單的運算式表單一起列出,可讓文法不允許可能令人困惑的程式碼,例如Treating array-creation-expression in this way, rather than listing it along with the other simple expression forms, enables the grammar to disallow potentially confusing code such as

object o = new int[3][1];

否則會解釋為which would otherwise be interpreted as

object o = (new int[3])[1];

常值Literals

Primary_expression ,其中包含 值 (值) 分類為值。A primary_expression that consists of a literal (Literals) is classified as a value.

插入字串Interpolated strings

Interpolated_string_expression 是由 $ 後面接著一般或逐字字串常值的正負號所組成,其中的洞以 { 和分隔,以 } 括住運算式和格式化規格。An interpolated_string_expression consists of a $ sign followed by a regular or verbatim string literal, wherein holes, delimited by { and }, enclose expressions and formatting specifications. 插入字串運算式是已細分為個別標記的 interpolated_string_literal 結果,如插入 字串常值中所述。An interpolated string expression is the result of an interpolated_string_literal that has been broken up into individual tokens, as described in Interpolated string literals.

interpolated_string_expression
    : '$' interpolated_regular_string
    | '$' interpolated_verbatim_string
    ;

interpolated_regular_string
    : interpolated_regular_string_whole
    | interpolated_regular_string_start interpolated_regular_string_body interpolated_regular_string_end
    ;

interpolated_regular_string_body
    : interpolation (interpolated_regular_string_mid interpolation)*
    ;

interpolation
    : expression
    | expression ',' constant_expression
    ;

interpolated_verbatim_string
    : interpolated_verbatim_string_whole
    | interpolated_verbatim_string_start interpolated_verbatim_string_body interpolated_verbatim_string_end
    ;

interpolated_verbatim_string_body
    : interpolation (interpolated_verbatim_string_mid interpolation)+
    ;

插補中的 constant_expression 必須隱含轉換成 intThe constant_expression in an interpolation must have an implicit conversion to int.

Interpolated_string_expression 分類為值。An interpolated_string_expression is classified as a value. 如果它立即轉換成 System.IFormattableSystem.FormattableString 使用隱含的插入字串轉換 (隱含 插入字串轉換) ,則插入字串運算式具有該類型。If it is immediately converted to System.IFormattable or System.FormattableString with an implicit interpolated string conversion (Implicit interpolated string conversions), the interpolated string expression has that type. 否則,它會有型別 stringOtherwise, it has the type string.

如果內插字串的類型是 System.IFormattableSystem.FormattableString ,則意義是的呼叫 System.Runtime.CompilerServices.FormattableStringFactory.CreateIf the type of an interpolated string is System.IFormattable or System.FormattableString, the meaning is a call to System.Runtime.CompilerServices.FormattableStringFactory.Create. 如果類型為 string ,則運算式的意義是的呼叫 string.FormatIf the type is string, the meaning of the expression is a call to string.Format. 在這兩種情況下,呼叫的引數清單都包含格式字串常值,其中包含每個插補的預留位置,以及對應至預留位置的每個運算式的引數。In both cases, the argument list of the call consists of a format string literal with placeholders for each interpolation, and an argument for each expression corresponding to the place holders.

格式字串常值的結構如下所示,其中 Ninterpolated_string_expression 中的插補數目:The format string literal is constructed as follows, where N is the number of interpolations in the interpolated_string_expression:

  • 如果 interpolated_regular_string_wholeinterpolated_verbatim_string_whole 遵循 $ 正負號,則格式字串常值就是該 token。If an interpolated_regular_string_whole or an interpolated_verbatim_string_whole follows the $ sign, then the format string literal is that token.
  • 否則,格式字串常值包含:Otherwise, the format string literal consists of:
    • 第一個 interpolated_regular_string_startinterpolated_verbatim_string_startFirst the interpolated_regular_string_start or interpolated_verbatim_string_start
    • 然後,針對每個數位 I 0 N-1Then for each number I from 0 to N-1:
      • 的十進位標記法 IThe decimal representation of I
      • 然後,如果對應的 插補constant_expression,則 , (逗點) ,後面接著 constant_expression 值的十進位標記法。Then, if the corresponding interpolation has a constant_expression, a , (comma) followed by the decimal representation of the value of the constant_expression
      • 然後, interpolated_regular_string_midinterpolated_regular_string_endinterpolated_verbatim_string_mid 或緊接在對應的插補之後的 interpolated_verbatim_string_endThen the interpolated_regular_string_mid, interpolated_regular_string_end, interpolated_verbatim_string_mid or interpolated_verbatim_string_end immediately following the corresponding interpolation.

後續的引數只是 插補 中的 運算式 (如有任何) 順序。The subsequent arguments are simply the expressions from the interpolations (if any), in order.

TODO:範例。TODO: examples.

簡單名稱Simple names

Simple_name 包含識別碼,並選擇性地接著類型引數清單:A simple_name consists of an identifier, optionally followed by a type argument list:

simple_name
    : identifier type_argument_list?
    ;

Simple_name 是表單 I 或表單的格式 I<A1,...,Ak> ,其中 I 是單一識別碼,而且 <A1,...,Ak> 是選擇性的 type_argument_listA simple_name is either of the form I or of the form I<A1,...,Ak>, where I is a single identifier and <A1,...,Ak> is an optional type_argument_list. 未指定任何 type_argument_list 時,請考慮為 K 零。When no type_argument_list is specified, consider K to be zero. Simple_name 的評估和分類方式如下:The simple_name is evaluated and classified as follows:

  • 如果 K 為零,且 simple_name 出現在 區塊 中,而且 區塊 的 (或封閉 區塊 的) 區域變數宣告空間 (宣告) 包含 區域變數、參數或具有名稱的常數  I ,則 simple_name 會參考該區域變數、參數或常數,並分類為變數或值。If K is zero and the simple_name appears within a block and if the block's (or an enclosing block's) local variable declaration space (Declarations) contains a local variable, parameter or constant with name I, then the simple_name refers to that local variable, parameter or constant and is classified as a variable or value.

  • 如果 K 為零,且 simple_name 出現在泛型方法宣告的主體中,且該宣告包含具有名稱的類型參數  I ,則 simple_name 會參考該類型參數。If K is zero and the simple_name appears within the body of a generic method declaration and if that declaration includes a type parameter with name I, then the simple_name refers to that type parameter.

  • 否則,針對每個實例類型  T (實例類型) ,從立即封入類型宣告的實例類型開始,然後繼續執行每個封入類別或結構宣告的實例類型 (如果有任何) :Otherwise, for each instance type T (The instance type), starting with the instance type of the immediately enclosing type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):

    • 如果 K 為零,且的宣告 T 包含具有名稱的型別參數  I ,則 simple_name 會參考該型別參數。If K is zero and the declaration of T includes a type parameter with name I, then the simple_name refers to that type parameter.
    • 否則,如果成員查閱 (成員查閱) 的 I in T 具有類型引數,則會 K   產生相符的結果:Otherwise, if a member lookup (Member lookup) of I in T with K type arguments produces a match:
      • 如果 T 是直接封入類別或結構類型的實例類型,而且查閱識別出一或多個方法,則結果為具有相關聯之實例運算式的方法群組 thisIf T is the instance type of the immediately enclosing class or struct type and the lookup identifies one or more methods, the result is a method group with an associated instance expression of this. 如果指定了類型引數清單,則會使用它來呼叫泛型方法, (方法調用) 。If a type argument list was specified, it is used in calling a generic method (Method invocations).
      • 否則,如果 T 是直接封入類別或結構類型的實例類型,如果查閱識別實例成員,且參考發生在實例函式的主體、實例方法或實例存取子內,則結果與表單的成員存取 (成員存取) 相同 this.IOtherwise, if T is the instance type of the immediately enclosing class or struct type, if the lookup identifies an instance member, and if the reference occurs within the body of an instance constructor, an instance method, or an instance accessor, the result is the same as a member access (Member access) of the form this.I. 只有當為零時,才會發生這種情況 KThis can only happen when K is zero.
      • 否則,結果會與表單或的成員存取 (成員存取) 相同 T.I T.I<A1,...,Ak>Otherwise, the result is the same as a member access (Member access) of the form T.I or T.I<A1,...,Ak>. 在此情況下,它是系結時期錯誤, simple_name 參考實例成員。In this case, it is a binding-time error for the simple_name to refer to an instance member.
  • 否則,針對每個命名空間  N ,從 simple_name 發生所在的命名空間開始,繼續每個封入命名空間 (如果有任何) ,並以全域命名空間結束,則會評估下列步驟,直到實體找到為止:Otherwise, for each namespace N, starting with the namespace in which the simple_name occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:

    • 如果 K 是零,且 I 是命名空間的名稱  N ,則:If K is zero and I is the name of a namespace in N, then:
      • 如果發生 simple_name 的位置是由命名空間宣告所括住, N 且命名空間宣告包含 extern_alias_directiveusing_alias_directive ,將名稱  I 與命名空間或類型產生關聯,則 simple_name 不明確,而且會發生編譯時期錯誤。If the location where the simple_name occurs is enclosed by a namespace declaration for N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.
      • 否則, simple_name 會參考中的命名空間 I NOtherwise, the simple_name refers to the namespace named I in N.
    • 否則,如果 N 包含具有 name 和 type 參數的可存取型別  I K   ,則:Otherwise, if N contains an accessible type having name I and K type parameters, then:
      • 如果 K 是零,且 simple_name 發生的位置是以命名空間宣告括住, N 且命名空間宣告包含 extern_alias_directiveusing_alias_directive ,將名稱  I 與命名空間或類型產生關聯,則 simple_name 不明確,而且會發生編譯時期錯誤。If K is zero and the location where the simple_name occurs is enclosed by a namespace declaration for N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.
      • 否則, namespace_or_type_name 會參考以指定的型別引數所構成的型別。Otherwise, the namespace_or_type_name refers to the type constructed with the given type arguments.
    • 否則,如果 simple_name 發生的位置是以的命名空間宣告括住  NOtherwise, if the location where the simple_name occurs is enclosed by a namespace declaration for N:
      • 如果 K 為零,且命名空間宣告包含 extern_alias_directiveusing_alias_directive ,將名稱  I 與匯入的命名空間或類型產生關聯,則 simple_name 會參考該命名空間或類型。If K is zero and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with an imported namespace or type, then the simple_name refers to that namespace or type.
      • 否則,如果由命名空間宣告的 using_namespace_directive s 和 using_static_directive s 所匯入的命名空間和類型宣告,只包含一個可存取的類型或具有名稱和類型參數的非延伸靜態成員  I ,則 K   simple_name 會參考以指定類型引數所建立的該類型或成員。Otherwise, if the namespaces and type declarations imported by the using_namespace_directive s and using_static_directive s of the namespace declaration contain exactly one accessible type or non-extension static member having name I and K type parameters, then the simple_name refers to that type or member constructed with the given type arguments.
      • 否則,如果命名空間宣告的 using_namespace_directive s 所匯入的命名空間和型別包含一個以上的可存取型別或非延伸方法靜態成員具有 name  IK   type 參數,則 simple_name 會不明確,而且會發生錯誤。Otherwise, if the namespaces and types imported by the using_namespace_directive s of the namespace declaration contain more than one accessible type or non-extension-method static member having name I and K type parameters, then the simple_name is ambiguous and an error occurs.

    請注意,這整個步驟與處理 namespace_or_type_name (命名空間和類型名稱) 的對應步驟完全相同。Note that this entire step is exactly parallel to the corresponding step in the processing of a namespace_or_type_name (Namespace and type names).

  • 否則, simple_name 是未定義的,而且會發生編譯時期錯誤。Otherwise, the simple_name is undefined and a compile-time error occurs.

括號運算式Parenthesized expressions

Parenthesized_expression 是由以括弧括住的 運算式 所組成。A parenthesized_expression consists of an expression enclosed in parentheses.

parenthesized_expression
    : '(' expression ')'
    ;

Parenthesized_expression 是藉由評估括弧內的 運算式 來評估。A parenthesized_expression is evaluated by evaluating the expression within the parentheses. 如果括弧內的 運算式 表示命名空間或類型,就會發生編譯階段錯誤。If the expression within the parentheses denotes a namespace or type, a compile-time error occurs. 否則, parenthesized_expression 的結果會是所含 運算式 的評估結果。Otherwise, the result of the parenthesized_expression is the result of the evaluation of the contained expression.

成員存取Member access

Member_accessprimary_expressionpredefined_typequalified_alias_member 所組成,後面接著 " . " token,後面接著一個 識別碼,可選擇性地接著 type_argument_listA member_access consists of a primary_expression, a predefined_type, or a qualified_alias_member, followed by a "." token, followed by an identifier, optionally followed by a type_argument_list.

member_access
    : primary_expression '.' identifier type_argument_list?
    | predefined_type '.' identifier type_argument_list?
    | qualified_alias_member '.' identifier
    ;

predefined_type
    : 'bool'   | 'byte'  | 'char'  | 'decimal' | 'double' | 'float' | 'int' | 'long'
    | 'object' | 'sbyte' | 'short' | 'string'  | 'uint'   | 'ulong' | 'ushort'
    ;

Qualified_alias_member 生產環境是在 命名空間別名限定詞中定義。The qualified_alias_member production is defined in Namespace alias qualifiers.

Member_access 的格式為表單 E.I 或形式 E.I<A1, ..., Ak> ,其中 E 是主要運算式、是 I 單一識別碼,而且 <A1, ..., Ak> 是選擇性的 type_argument_listA member_access is either of the form E.I or of the form E.I<A1, ..., Ak>, where E is a primary-expression, I is a single identifier and <A1, ..., Ak> is an optional type_argument_list. 未指定任何 type_argument_list 時,請考慮為 K 零。When no type_argument_list is specified, consider K to be zero.

具有類型之 primary_expressionmember_access 會動態系結 dynamic (動態繫結) 。A member_access with a primary_expression of type dynamic is dynamically bound (Dynamic binding). 在此情況下,編譯器會將成員存取分類為型別的屬性存取 dynamicIn this case the compiler classifies the member access as a property access of type dynamic. 以下規則會在執行時間使用執行時間類型,而不是 primary_expression 的編譯時間類型,以判斷 member_access 的意義。The rules below to determine the meaning of the member_access are then applied at run-time, using the run-time type instead of the compile-time type of the primary_expression. 如果這個執行時間分類會導致方法群組,則成員存取必須是 invocation_expressionprimary_expressionIf this run-time classification leads to a method group, then the member access must be the primary_expression of an invocation_expression.

Member_access 的評估和分類方式如下:The member_access is evaluated and classified as follows:

  • 如果 K 是零,而且 E 是命名空間且 E 包含具有名稱的嵌套命名空間  I ,則結果為該命名空間。If K is zero and E is a namespace and E contains a nested namespace with name I, then the result is that namespace.
  • 否則,如果 E 是命名空間,而且 E 包含具有名稱和類型參數的可存取型別  I ,則 K   結果會是以指定的型別引數所構成的型別。Otherwise, if E is a namespace and E contains an accessible type having name I and K type parameters, then the result is that type constructed with the given type arguments.
  • 如果 Epredefined_type 或分類為類型的 primary_expression ,如果不是 E 型別參數,而且如果成員查閱 (的成員查閱) 中的 I 型別 E K   參數產生相符的,則 E.I 會進行評估並分類如下:If E is a predefined_type or a primary_expression classified as a type, if E is not a type parameter, and if a member lookup (Member lookup) of I in E with K type parameters produces a match, then E.I is evaluated and classified as follows:
    • 如果 I 識別型別,則結果是以指定的型別引數所構成的型別。If I identifies a type, then the result is that type constructed with the given type arguments.
    • 如果 I 識別一或多個方法,則結果為沒有相關聯實例運算式的方法群組。If I identifies one or more methods, then the result is a method group with no associated instance expression. 如果指定了類型引數清單,則會使用它來呼叫泛型方法, (方法調用) 。If a type argument list was specified, it is used in calling a generic method (Method invocations).
    • 如果 I 識別 static 屬性,則結果會是沒有相關聯的實例運算式的屬性存取。If I identifies a static property, then the result is a property access with no associated instance expression.
    • 如果 I 識別 static 欄位:If I identifies a static field:
      • 如果欄位是 readonly ,而參考出現在宣告欄位之類別或結構的靜態函式之外,則結果為值,亦即中靜態欄位的值  I  EIf the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.
      • 否則,結果會是變數,也就是中的靜態欄位  I  EOtherwise, the result is a variable, namely the static field I in E.
    • 如果 I 識別 static 事件:If I identifies a static event:
      • 如果參考出現在宣告事件的類別或結構中,且事件已在沒有 event_accessor_declarations (事件) 的情況下宣告,則 E.I 會完全如同 I 靜態欄位一樣處理。If the reference occurs within the class or struct in which the event is declared, and the event was declared without event_accessor_declarations (Events), then E.I is processed exactly as if I were a static field.
      • 否則,結果會是沒有相關聯的實例運算式的事件存取。Otherwise, the result is an event access with no associated instance expression.
    • 如果 I 識別常數,則結果為值,亦即該常數的值。If I identifies a constant, then the result is a value, namely the value of that constant.
    • 如果 I 識別列舉成員,則結果為值,亦即該列舉成員的值。If I identifies an enumeration member, then the result is a value, namely the value of that enumeration member.
    • 否則, E.I 就是不正確成員參考,而且會發生編譯時期錯誤。Otherwise, E.I is an invalid member reference, and a compile-time error occurs.
  • 如果 E 是屬性存取、索引子存取、變數或值、的型別為的型別,  T 而 with 型別引數的成員查閱 (成員查閱) 會 I T K   產生相符的 E.I 結果,則會進行評估並分類如下:If E is a property access, indexer access, variable, or value, the type of which is T, and a member lookup (Member lookup) of I in T with K type arguments produces a match, then E.I is evaluated and classified as follows:
    • 首先,如果 E 是屬性或索引子存取,則會取得 (運算式值) 的屬性或索引子存取值,並將其重新分類 E 為值。First, if E is a property or indexer access, then the value of the property or indexer access is obtained (Values of expressions) and E is reclassified as a value.
    • 如果 I 識別一或多個方法,則結果為具有相關聯之實例運算式的方法群組 EIf I identifies one or more methods, then the result is a method group with an associated instance expression of E. 如果指定了類型引數清單,則會使用它來呼叫泛型方法, (方法調用) 。If a type argument list was specified, it is used in calling a generic method (Method invocations).
    • 如果 I 識別實例屬性,If I identifies an instance property,
      • 如果 Ethis ,則會 I 識別自動執行的屬性 (自動 實) 沒有 setter 的屬性,而且在類別或結構類型的實例的函式中發生參考, T 則結果為變數,也就是由 I 指定之實例中所指定之 auto 屬性的隱藏支援欄位 T thisIf E is this, I identifies an automatically implemented property (Automatically implemented properties) without a setter, and the reference occurs within an instance constructor for a class or struct type T, then the result is a variable, namely the hidden backing field for the auto-property given by I in the instance of T given by this.
      • 否則,結果會是具有相關聯的實例運算式的屬性存取  EOtherwise, the result is a property access with an associated instance expression of E.
    • 如果 Tclass_type ,並 I 識別該 class_type 的實例欄位:If T is a class_type and I identifies an instance field of that class_type:
      • 如果的值 E 為,則會擲回 null System.NullReferenceExceptionIf the value of E is null, then a System.NullReferenceException is thrown.
      • 否則,如果欄位是 readonly ,而參考出現在宣告欄位之類別的實例函式之外,則結果為值,也就是所參考之物件中的域值  I  EOtherwise, if the field is readonly and the reference occurs outside an instance constructor of the class in which the field is declared, then the result is a value, namely the value of the field I in the object referenced by E.
      • 否則,結果會是變數,也就是所  I 參考之物件中的欄位  EOtherwise, the result is a variable, namely the field I in the object referenced by E.
    • 如果 Tstruct_type ,並 I 識別該 struct_type 的實例欄位:If T is a struct_type and I identifies an instance field of that struct_type:
      • 如果 E 是值,或如果欄位是, readonly 而參考出現在宣告欄位之結構的實例函式之外,則結果為值,也就是  I 指定之結構實例中的欄位值  EIf E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.
      • 否則,結果會是變數,也就是  I 指定之結構實例中的欄位  EOtherwise, the result is a variable, namely the field I in the struct instance given by E.
    • 如果 I 識別實例事件:If I identifies an instance event:
      • 如果參考出現在宣告事件的類別或結構中,且事件未 event_accessor_declarations (事件) 的情況下宣告,而且參考不會在或運算子的左邊進行 += ,則 -= E.I 會如同 I 實例欄位一樣處理該參考。If the reference occurs within the class or struct in which the event is declared, and the event was declared without event_accessor_declarations (Events), and the reference does not occur as the left-hand side of a += or -= operator, then E.I is processed exactly as if I was an instance field.
      • 否則,結果會是具有相關聯之實例運算式的事件存取  EOtherwise, the result is an event access with an associated instance expression of E.
  • 否則,系統會嘗試將 E.I (擴充方法 調用) 的擴充方法調用進行處理。Otherwise, an attempt is made to process E.I as an extension method invocation (Extension method invocations). 如果失敗, E.I 是不正確成員參考,且發生系結階段錯誤。If this fails, E.I is an invalid member reference, and a binding-time error occurs.

相同的簡單名稱和類型名稱Identical simple names and type names

在表單的成員存取中 E.I ,如果 E 是單一識別碼,而且如果 E Simple_name 的意義 (簡單名稱) 是常數、欄位、屬性、區域變數或參數,其類型與 E type_name (命名空間和類型名稱 的意義相同,則允許這兩種可能的含意 EIn a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple_name (Simple names) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type_name (Namespace and type names), then both possible meanings of E are permitted. 這兩種可能的含意 E.I 永遠不可混淆,因為 I E 在這兩種情況下都必須是類型的成員。The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. 換句話說,此規則只會允許存取靜態成員,以及可能 E 發生編譯時期錯誤的巢狀型別。In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred. 例如:For example:

struct Color
{
    public static readonly Color White = new Color(...);
    public static readonly Color Black = new Color(...);

    public Color Complement() {...}
}

class A
{
    public Color Color;                // Field Color of type Color

    void F() {
        Color = Color.Black;           // References Color.Black static member
        Color = Color.Complement();    // Invokes Complement() on Color field
    }

    static void G() {
        Color c = Color.White;         // References Color.White static member
    }
}

文法多義性Grammar ambiguities

Simple_name (簡單名稱的生產) 和 Member_access (成員存取) 可能會在運算式的文法中提供明顯的多義性。The productions for simple_name (Simple names) and member_access (Member access) 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 序列 (為 simple_name (簡單名稱) 、 member_access (成員存取) ,或 Pointer_member_access (指標成員存取) 以 type_argument_list ( 類型引數) 結尾,則會檢查緊接在結尾 token 之後的權杖 >If a sequence of tokens can be parsed (in context) as a simple_name (Simple names), member_access (Member access), or pointer_member_access (Pointer member access) ending with a type_argument_list (Type arguments), the token immediately following the closing > token is examined. 如果是下列其中一項If it is one of

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

然後, type_argument_list 會保留為 simple_namemember_accesspointer_member_access 的一部分,而且會捨棄權杖序列順序的任何其他可能剖析。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. 否則,即使沒有其他可能的權杖順序剖析, type_argument_list 也不會被視為 simple_namemember_accesspointer_member_access 的一部分。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. 請注意,在 namespace_or_type_name (命名空間和類型名稱) 中剖析 type_argument_list 時,不會套用這些規則。Note that these rules are not applied when parsing a type_argument_list in a namespace_or_type_name (Namespace and type names). 陳述式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) ,而不是 type_argument_list 後面接著二元加號運算子的 simple_namewill 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> 會以 type_argument_listnamespace_or_type_name 來解讀。the tokens C<T> are interpreted as a namespace_or_type_name with a type_argument_list.

叫用運算式Invocation expressions

用來叫用方法的 invocation_expressionAn invocation_expression is used to invoke a method.

invocation_expression
    : primary_expression '(' argument_list? ')'
    ;

如果至少有下列其中一個保存, invocation_expression 會動態系結 (動態繫結) :An invocation_expression is dynamically bound (Dynamic binding) if at least one of the following holds:

  • Primary_expression 具有編譯時間類型 dynamicThe primary_expression has compile-time type dynamic.
  • 選擇性 argument_list 至少有一個引數具有編譯時間類型 dynamic ,而且 primary_expression 沒有委派類型。At least one argument of the optional argument_list has compile-time type dynamic and the primary_expression does not have a delegate type.

在此情況下,編譯器會將 invocation_expression 分類為類型的值 dynamicIn this case the compiler classifies the invocation_expression as a value of type dynamic. 以下規則會在執行時間使用執行時間類型,而不是具有編譯時間類型之 primary_expression 和引數的編譯時間類型,以判斷 invocation_expression 的意義 dynamicThe rules below to determine the meaning of the invocation_expression are then applied at run-time, using the run-time type instead of the compile-time type of those of the primary_expression and arguments which have the compile-time type dynamic. 如果 primary_expression 沒有編譯時間型別 dynamic ,則方法調用會進行有限的編譯時間檢查,如動態多載 解析的編譯時間檢查中所述。If the primary_expression does not have compile-time type dynamic, then the method invocation undergoes a limited compile time check as described in Compile-time checking of dynamic overload resolution.

Invocation_expressionprimary_expression 必須是方法群組或 delegate_type 的值。The primary_expression of an invocation_expression must be a method group or a value of a delegate_type. 如果 primary_expression 是方法群組,則 invocation_expression 是方法調用, (方法 調用) 。If the primary_expression is a method group, the invocation_expression is a method invocation (Method invocations). 如果 primary_expressiondelegate_type 的值,則 invocation_expression 為委派調用, (委派調用) 。If the primary_expression is a value of a delegate_type, the invocation_expression is a delegate invocation (Delegate invocations). 如果 primary_expression 不是方法群組,也不是 delegate_type 的值,則會發生系結階段錯誤。If the primary_expression is neither a method group nor a value of a delegate_type, a binding-time error occurs.

選擇性的 argument_list (引數清單) 提供方法參數的值或變數參考。The optional argument_list (Argument lists) provides values or variable references for the parameters of the method.

評估 invocation_expression 的結果分類如下:The result of evaluating an invocation_expression is classified as follows:

  • 如果 invocation_expression 叫用傳回的方法或委派 void ,則結果為 nothing。If the invocation_expression invokes a method or delegate that returns void, the result is nothing. 分類為 nothing 的運算式只允許在 statement_expression (運算式語句 的內容中) 或作為 lambda_expression (匿名函式 運算式 的主體) 。An expression that is classified as nothing is permitted only in the context of a statement_expression (Expression statements) or as the body of a lambda_expression (Anonymous function expressions). 否則,就會發生系結階段錯誤。Otherwise a binding-time error occurs.
  • 否則,結果會是方法或委派所傳回之類型的值。Otherwise, the result is a value of the type returned by the method or delegate.

方法調用Method invocations

若為方法調用, invocation_expressionprimary_expression 必須是方法群組。For a method invocation, the primary_expression of the invocation_expression must be a method group. 方法群組會識別要叫用的一個方法或一組多載的方法,以從中選擇要叫用的特定方法。The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke. 在後者的情況下,判斷要叫用的特定方法是根據 argument_list 中的引數類型所提供的內容。In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the argument_list.

表單方法調用的系結時間處理 M(A) (其中 M 是方法群組 (可能包含 type_argument_list) ,而 A 是選擇性的 argument_list,包含下列步驟:The binding-time processing of a method invocation of the form M(A), where M is a method group (possibly including a type_argument_list), and A is an optional argument_list, consists of the following steps:

  • 會建立方法調用的候選方法集合。The set of candidate methods for the method invocation is constructed. 針對 F 與方法群組相關聯的每個方法 MFor each method F associated with the method group M:
    • 如果 F 是非泛型,則在下列情況下 F 為候選:If F is non-generic, F is a candidate when:
    • 如果 F 是泛型且沒有 M 類型引數清單, F 則在下列情況下是候選項:If F is generic and M has no type argument list, F is a candidate when:
      • 型別推斷 (類型推斷) 成功、推斷呼叫類型引數的清單,以及Type inference (Type inference) succeeds, inferring a list of type arguments for the call, and
      • 當推斷的型別引數取代為對應的方法型別參數時,F 的參數清單中的所有結構化型別都會滿足其條件約束, (符合) 的 條件約束 ,而的參數清單 F 適用于 A (適用的函數成員) 。Once the inferred type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of F satisfy their constraints (Satisfying constraints), and the parameter list of F is applicable with respect to A (Applicable function member).
    • 如果 F 是泛型且 M 包含型別引數清單,則在下列情況下 F 是候選項:If F is generic and M includes a type argument list, F is a candidate when:
      • F 具有與類型引數清單中所提供之相同的方法類型參數數目,以及F has the same number of method type parameters as were supplied in the type argument list, and
      • 一旦將型別引數替換為對應的方法型別參數,F 的參數清單中的所有結構化型別就會滿足其條件約束, (符合) 的 條件約束 ,而的參數清單 F 適用于 A (適用的函數成員) 。Once the type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of F satisfy their constraints (Satisfying constraints), and the parameter list of F is applicable with respect to A (Applicable function member).
  • 這組候選方法會縮減成隻包含來自最多衍生類型的方法:針對集合中的每個方法 C.F ,其中 C 是宣告方法的類型, F 在基底類型中宣告的所有方法 C 都會從集合中移除。The set of candidate methods is reduced to contain only methods from the most derived types: For each method C.F in the set, where C is the type in which the method F is declared, all methods declared in a base type of C are removed from the set. 此外,如果 C 是以外的類別型別 object ,則會從集合中移除在介面型別中宣告的所有方法。Furthermore, if C is a class type other than object, all methods declared in an interface type are removed from the set. (這個第二個規則只有當方法群組是成員查閱的結果,而該型別參數具有物件以外的有效基類和非空白的有效介面集時,才會有影響。 ) (This latter rule only has affect when the method group was the result of a member lookup on a type parameter having an effective base class other than object and a non-empty effective interface set.)
  • 如果產生的候選方法集合是空的,則會放棄下列步驟的進一步處理,而改為嘗試將叫用視為擴充方法調用, (擴充方法 調用) 。If the resulting set of candidate methods is empty, then further processing along the following steps are abandoned, and instead an attempt is made to process the invocation as an extension method invocation (Extension method invocations). 如果失敗,就不會有任何適用的方法存在,而且會發生系結階段錯誤。If this fails, then no applicable methods exist, and a binding-time error occurs.
  • 您可以使用多載解析規則的多 載解析規則來識別候選方法集合的最佳方法。The best method of the set of candidate methods is identified using the overload resolution rules of Overload resolution. 如果無法識別單一最佳方法,則方法調用會不明確,而且會發生系結階段錯誤。If a single best method cannot be identified, the method invocation is ambiguous, and a binding-time error occurs. 執行多載解析時,會在將類型引數替換為對應方法類型參數的 (提供或推斷) 之後,考慮泛型方法的參數。When performing overload resolution, the parameters of a generic method are considered after substituting the type arguments (supplied or inferred) for the corresponding method type parameters.
  • 執行所選最佳方法的最終驗證:Final validation of the chosen best method is performed:
    • 方法會在方法群組的內容中進行驗證:如果最理想的方法是靜態方法,則方法群組必須由 simple_namemember_access 透過型別產生。The method is validated in the context of the method group: If the best method is a static method, the method group must have resulted from a simple_name or a member_access through a type. 如果最佳方法是實例方法,則方法群組必須由 simple_name、透過變數或值 member_access ,或 base_access 所產生。If the best method is an instance method, the method group must have resulted from a simple_name, a member_access through a variable or value, or a base_access. 如果上述兩項需求都不成立,就會發生系結階段錯誤。If neither of these requirements is true, a binding-time error occurs.
    • 如果最理想的方法是泛型方法, (提供或) 推斷的型別引數會根據條件約束進行檢查, (滿足泛型方法) 所宣告的 條件約束If the best method is a generic method, the type arguments (supplied or inferred) are checked against the constraints (Satisfying constraints) declared on the generic method. 如果任何類型引數無法滿足類型參數上 (s) 的對應條件約束,就會發生系結階段錯誤。If any type argument does not satisfy the corresponding constraint(s) on the type parameter, a binding-time error occurs.

在上述步驟的系結時間選取並驗證方法之後,就會根據動態多載 解析的編譯時間檢查中所述的函數成員調用規則,來處理實際的執行時間調用。Once a method has been selected and validated at binding-time by the above steps, the actual run-time invocation is processed according to the rules of function member invocation described in Compile-time checking of dynamic overload resolution.

上述解析規則的直覺效果如下所示:若要找出方法調用所叫用的特定方法,請從方法調用所指示的型別開始,然後繼續繼承鏈,直到找到至少一個適用、可存取的非覆寫方法宣告為止。The intuitive effect of the resolution rules described above is as follows: To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. 然後,在該型別中宣告的適用、可存取、非覆寫方法集合上執行型別推斷和多載解析,然後再叫用選取的方法。Then perform type inference and overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected. 如果找不到方法,請改為將叫用視為擴充方法叫用來處理。If no method was found, try instead to process the invocation as an extension method invocation.

擴充方法調用Extension method invocations

在方法調用中 (在其中一個表單) 的已封裝實例上調用In a method invocation (Invocations on boxed instances) of one of the forms

expr . identifier ( )

expr . identifier ( args )

expr . identifier < typeargs > ( )

expr . identifier < typeargs > ( args )

如果調用的正常處理找不到任何適用的方法,則會嘗試將結構視為擴充方法調用來處理。if the normal processing of the invocation finds no applicable methods, an attempt is made to process the construct as an extension method invocation. 如果 expr 或任何引數 都有編譯 時間類型 dynamic ,將不會套用擴充方法。If expr or any of the args has compile-time type dynamic, extension methods will not apply.

目標是要尋找最佳的 type_name C ,以便進行對應的靜態方法調用:The objective is to find the best type_name C, so that the corresponding static method invocation can take place:

C . identifier ( expr )

C . identifier ( expr , args )

C . identifier < typeargs > ( expr )

C . identifier < typeargs > ( expr , args )

擴充方法 Ci.Mj 符合下列 條件An extension method Ci.Mj is eligible if:

  • Ci 是非泛型、非嵌套類別Ci is a non-generic, non-nested class
  • 的名稱 Mj識別碼The name of Mj is identifier
  • Mj 適用于將引數套用至引數作為靜態方法時,如上所示Mj is accessible and applicable when applied to the arguments as a static method as shown above
  • expr 到的第一個參數型別,都有隱含的識別、參考或裝箱轉換 MjAn implicit identity, reference or boxing conversion exists from expr to the type of the first parameter of Mj.

搜尋會繼續進行,如下所示 CThe search for C proceeds as follows:

  • 從最接近的封入命名空間宣告開始,繼續每個封入命名空間宣告,並以包含的編譯單位結束,就會進行後續的嘗試,以找出一組候選的擴充方法:Starting with the closest enclosing namespace declaration, continuing with each enclosing namespace declaration, and ending with the containing compilation unit, successive attempts are made to find a candidate set of extension methods:
    • 如果指定的命名空間或編譯單位直接包含具有合格延伸方法的非泛型型別宣告 Ci Mj ,則這些擴充方法的集合就是候選集合。If the given namespace or compilation unit directly contains non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
    • 如果 using_static_declarations 匯入的型別 Ci ,以及在指定的命名空間或編譯單位中 using_namespace_directive s 匯入之命名空間中直接宣告的型別包含合格的擴充方法 Mj ,則這些擴充方法的集合就是候選集合。If types Ci imported by using_static_declarations and directly declared in namespaces imported by using_namespace_directive s in the given namespace or compilation unit directly contain eligible extension methods Mj, then the set of those extension methods is the candidate set.
  • 如果在任何封入命名空間宣告或編譯單位中都找不到候選集合,則會發生編譯時期錯誤。If no candidate set is found in any enclosing namespace declaration or compilation unit, a compile-time error occurs.
  • 否則,多載解析會套用至候選集合,如 (多載 解析) 中所述。Otherwise, overload resolution is applied to the candidate set as described in (Overload resolution). 如果找不到單一最佳方法,就會發生編譯時期錯誤。If no single best method is found, a compile-time error occurs.
  • C 這是在其中將最佳方法宣告為擴充方法的型別。C is the type within which the best method is declared as an extension method.

使用 C 做為目標時,方法呼叫接著會處理為靜態方法調用, (動態多載解析) 的編譯時期檢查Using C as a target, the method call is then processed as a static method invocation (Compile-time checking of dynamic overload resolution).

上述規則表示實例方法優先于擴充方法,內部命名空間宣告中提供的擴充方法優先于外部命名空間宣告中可用的擴充方法,而且直接在命名空間中宣告的擴充方法,優先于使用命名空間指示詞彙入至該相同命名空間的擴充方法。The preceding rules mean that instance methods take precedence over extension methods, that extension methods available in inner namespace declarations take precedence over extension methods available in outer namespace declarations, and that extension methods declared directly in a namespace take precedence over extension methods imported into that same namespace with a using namespace directive. 例如:For example:

public static class E
{
    public static void F(this object obj, int i) { }

    public static void F(this object obj, string s) { }
}

class A { }

class B
{
    public void F(int i) { }
}

class C
{
    public void F(object obj) { }
}

class X
{
    static void Test(A a, B b, C c) {
        a.F(1);              // E.F(object, int)
        a.F("hello");        // E.F(object, string)

        b.F(1);              // B.F(int)
        b.F("hello");        // E.F(object, string)

        c.F(1);              // C.F(object)
        c.F("hello");        // C.F(object)
    }
}

在此範例中, B 的方法優先于第一個擴充方法,而 C 的方法優先于這兩種擴充方法。In the example, B's method takes precedence over the first extension method, and C's method takes precedence over both extension methods.

public static class C
{
    public static void F(this int i) { Console.WriteLine("C.F({0})", i); }
    public static void G(this int i) { Console.WriteLine("C.G({0})", i); }
    public static void H(this int i) { Console.WriteLine("C.H({0})", i); }
}

namespace N1
{
    public static class D
    {
        public static void F(this int i) { Console.WriteLine("D.F({0})", i); }
        public static void G(this int i) { Console.WriteLine("D.G({0})", i); }
    }
}

namespace N2
{
    using N1;

    public static class E
    {
        public static void F(this int i) { Console.WriteLine("E.F({0})", i); }
    }

    class Test
    {
        static void Main(string[] args)
        {
            1.F();
            2.G();
            3.H();
        }
    }
}

此範例的輸出為:The output of this example is:

E.F(1)
D.G(2)
C.H(3)

D.G 優先于 C.G ,且優先 E.FD.FC.FD.G takes precedence over C.G, and E.F takes precedence over both D.F and C.F.

委派調用Delegate invocations

若為委派調用, invocation_expressionprimary_expression 必須是 delegate_type 的值。For a delegate invocation, the primary_expression of the invocation_expression must be a value of a delegate_type. 此外,考慮 delegate_type 是與 delegate_type 具有相同參數清單的函式成員, delegate_type 必須 (適用 于) argument_list 的相關函式 成員 invocation_expression。Furthermore, considering the delegate_type to be a function member with the same parameter list as the delegate_type, the delegate_type must be applicable (Applicable function member) with respect to the argument_list of the invocation_expression.

表單的委派調用執行時間處理 D(A) ,其中 Ddelegate_typeprimary_expression ,而 A 是選擇性的 argument_list,其中包含下列步驟:The run-time processing of a delegate invocation of the form D(A), where D is a primary_expression of a delegate_type and A is an optional argument_list, consists of the following steps:

  • D 會進行評估。D is evaluated. 如果此評估造成例外狀況,則不會執行任何進一步的步驟。If this evaluation causes an exception, no further steps are executed.
  • 的值 D 會被檢查為有效。The value of D is checked to be valid. 如果的值 Dnull ,則會擲回, System.NullReferenceException 且不會執行任何進一步的步驟。If the value of D is null, a System.NullReferenceException is thrown and no further steps are executed.
  • 否則, D 就是委派實例的參考。Otherwise, D is a reference to a delegate instance. 函數成員調用 (動態多載解析的編譯時間檢查) 是在委派的調用清單中的每個可呼叫實體上執行。Function member invocations (Compile-time checking of dynamic overload resolution) are performed on each of the callable entities in the invocation list of the delegate. 針對由實例和實例方法所組成的可呼叫實體,調用的實例是包含在可呼叫實體中的實例。For callable entities consisting of an instance and instance method, the instance for the invocation is the instance contained in the callable entity.

項目存取Element access

Element_accessprimary_no_array_creation_expression 所組成,後面接著 " [ " token,後面接著 argument_list,後面接著 " ] " token。An element_access consists of a primary_no_array_creation_expression, followed by a "[" token, followed by an argument_list, followed by a "]" token. Argument_list 是由一或多個 引數(以逗號分隔)所組成。The argument_list consists of one or more argument s, separated by commas.

element_access
    : primary_no_array_creation_expression '[' expression_list ']'
    ;

Element_accessargument_list 不能包含 refout 引數。The argument_list of an element_access is not allowed to contain ref or out arguments.

如果至少有下列其中一個保存, element_access 會動態系結 (動態繫結) :An element_access is dynamically bound (Dynamic binding) if at least one of the following holds:

  • Primary_no_array_creation_expression 具有編譯時間類型 dynamicThe primary_no_array_creation_expression has compile-time type dynamic.
  • Argument_list 至少有一個運算式具有編譯時間類型 dynamic ,而且 primary_no_array_creation_expression 沒有陣列類型。At least one expression of the argument_list has compile-time type dynamic and the primary_no_array_creation_expression does not have an array type.

在此情況下,編譯器會將 element_access 分類為類型的值 dynamicIn this case the compiler classifies the element_access as a value of type dynamic. 下列規則可判斷 element_access 的意義,接著會在執行時間套用執行時間類型,而不是 primary_no_array_creation_expression 的編譯時間類型,以及具有編譯時間類型之 argument_list 運算式的編譯時間類型 dynamicThe rules below to determine the meaning of the element_access are then applied at run-time, using the run-time type instead of the compile-time type of those of the primary_no_array_creation_expression and argument_list expressions which have the compile-time type dynamic. 如果 primary_no_array_creation_expression 沒有編譯時間類型,則專案存取權會依照動態多載 dynamic 解析的編譯時間檢查所述,進行有限的編譯時間檢查。If the primary_no_array_creation_expression does not have compile-time type dynamic, then the element access undergoes a limited compile time check as described in Compile-time checking of dynamic overload resolution.

如果 element_accessprimary_no_array_creation_expressionarray_type 的值,則 element_access 是陣列存取 (陣列存取) 。If the primary_no_array_creation_expression of an element_access is a value of an array_type, the element_access is an array access (Array access). 否則, primary_no_array_creation_expression 必須是具有一或多個索引子成員之類別、結構或介面類別型的變數或值,在此情況下, element_access 是索引子存取 (索引子存取) 。Otherwise, the primary_no_array_creation_expression must be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the element_access is an indexer access (Indexer access).

陣列存取Array access

針對陣列存取, element_accessprimary_no_array_creation_expression 必須是 array_type 的值。For an array access, the primary_no_array_creation_expression of the element_access must be a value of an array_type. 此外,陣列存取的 argument_list 不能包含具名引數。 Argument_list 中的運算式數目必須與 array_type 的等級相同,而且每個運算式的類型必須是、、、 int 或,而且 uint long ulong 必須可以隱含地轉換成這些類型的一或多個。Furthermore, the argument_list of an array access is not allowed to contain named arguments.The number of expressions in the argument_list must be the same as the rank of the array_type, and each expression must be of type int, uint, long, ulong, or must be implicitly convertible to one or more of these types.

評估陣列存取的結果是陣列的元素類型變數,也就是 argument_list 中) 的運算式 (的值所選取的陣列元素 () s。The result of evaluating an array access is a variable of the element type of the array, namely the array element selected by the value(s) of the expression(s) in the argument_list.

表單存取的執行時間處理 P[A] ,其中 Parray_typeprimary_no_array_creation_expression ,而 A 是一個 argument_list,其中包含下列步驟:The run-time processing of an array access of the form P[A], where P is a primary_no_array_creation_expression of an array_type and A is an argument_list, consists of the following steps:

  • P 會進行評估。P is evaluated. 如果此評估造成例外狀況,則不會執行任何進一步的步驟。If this evaluation causes an exception, no further steps are executed.
  • Argument_list 的索引運算式會依序從左至右進行評估。The index expressions of the argument_list are evaluated in order, from left to right. 在評估每個索引運算式之後,會執行隱含轉換 (隱含 轉換) 為下列其中一種類型: intuintlongulongFollowing evaluation of each index expression, an implicit conversion (Implicit conversions) to one of the following types is performed: int, uint, long, ulong. 此清單中的第一個類型會選擇隱含轉換存在。The first type in this list for which an implicit conversion exists is chosen. 比方說,如果索引運算式的型別為 short int ,則會執行隱含轉換,因為可能會將隱含轉換成 short intshort from longFor instance, if the index expression is of type short then an implicit conversion to int is performed, since implicit conversions from short to int and from short to long are possible. 如果索引運算式的評估或後續的隱含轉換造成例外狀況,則不會評估進一步的索引運算式,也不會執行任何進一步的步驟。If evaluation of an index expression or the subsequent implicit conversion causes an exception, then no further index expressions are evaluated and no further steps are executed.
  • 的值 P 會被檢查為有效。The value of P is checked to be valid. 如果的值 Pnull ,則會擲回, System.NullReferenceException 且不會執行任何進一步的步驟。If the value of P is null, a System.NullReferenceException is thrown and no further steps are executed.
  • 會根據所參考陣列實例的每個維度的實際界限,檢查 argument_list 中每個運算式的值 PThe value of each expression in the argument_list is checked against the actual bounds of each dimension of the array instance referenced by P. 如果有一或多個值超出範圍,則會擲回, System.IndexOutOfRangeException 且不會執行任何進一步的步驟。If one or more values are out of range, a System.IndexOutOfRangeException is thrown and no further steps are executed.
  • 索引運算式 (s) 所提供的陣列元素位置會計算出來,而且這個位置會成為陣列存取的結果。The location of the array element given by the index expression(s) is computed, and this location becomes the result of the array access.

索引子存取Indexer access

針對索引子存取, element_accessprimary_no_array_creation_expression 必須是類別、結構或介面型別的變數或值,而且此型別必須執行一或多個適用于 element_accessargument_list 的索引子。For an indexer access, the primary_no_array_creation_expression of the element_access must be a variable or value of a class, struct, or interface type, and this type must implement one or more indexers that are applicable with respect to the argument_list of the element_access.

形式的索引子存取的系結時間處理 P[A] ,其中 P 是類別、結構或介面型別的 primary_no_array_creation_expression T ,而 Aargument_list,包含下列步驟:The binding-time processing of an indexer access of the form P[A], where P is a primary_no_array_creation_expression of a class, struct, or interface type T, and A is an argument_list, consists of the following steps:

  • 由所提供的索引子集 TThe set of indexers provided by T is constructed. 此集合包含在中宣告的所有索引子, T 或的基底類型 T 不是宣告 override ,而且可以在目前的內容中存取 (成員存取) 。The set consists of all indexers declared in T or a base type of T that are not override declarations and are accessible in the current context (Member access).
  • 此集合會縮減為適用且不被其他索引子隱藏的索引子。The set is reduced to those indexers that are applicable and not hidden by other indexers. 下列規則會套用至集合中的每個索引子 S.I ,其中是用來宣告 S 索引子的類型 IThe following rules are applied to each indexer S.I in the set, where S is the type in which the indexer I is declared:
    • 如果不適 I 用於 A (適用的函數成員) ,則 I 會從集合中移除。If I is not applicable with respect to A (Applicable function member), then I is removed from the set.
    • 如果 I 適用于 (適用的函式 A 成員) ,則基底類型中宣告的所有索引子 S 都會從集合中移除。If I is applicable with respect to A (Applicable function member), then all indexers declared in a base type of S are removed from the set.
    • 如果 I 適用于 (適用的函式 A 成員) ,而且 S 是以外的類別型別 object ,則會從集合中移除所有在介面中宣告的索引子。If I is applicable with respect to A (Applicable function member) and S is a class type other than object, all indexers declared in an interface are removed from the set.
  • 如果產生的候選索引子集合是空的,則不存在任何適用的索引子,而且會發生系結階段錯誤。If the resulting set of candidate indexers is empty, then no applicable indexers exist, and a binding-time error occurs.
  • 您可以使用多載解析規則的多 載解析規則來識別候選索引子集的最佳索引子。The best indexer of the set of candidate indexers is identified using the overload resolution rules of Overload resolution. 如果無法識別單一的最佳索引子,索引子存取就不明確,而且會發生系結階段錯誤。If a single best indexer cannot be identified, the indexer access is ambiguous, and a binding-time error occurs.
  • Argument_list 的索引運算式會依序從左至右進行評估。The index expressions of the argument_list are evaluated in order, from left to right. 處理索引子存取的結果是分類為索引子存取的運算式。The result of processing the indexer access is an expression classified as an indexer access. 索引子存取運算式會參考上一個步驟中所決定的索引子,而且有相關聯的實例運算式 P 和相關聯的引數清單 AThe indexer access expression references the indexer determined in the step above, and has an associated instance expression of P and an associated argument list of A.

視使用的內容而定,索引子存取會造成 get 存取 子或索引子 set 存取 子的調用。Depending on the context in which it is used, an indexer access causes invocation of either the get accessor or the set accessor of the indexer. 如果索引子存取是指派的目標,則會叫用 set 存取 子,以 (簡單指派) 指派新值。If the indexer access is the target of an assignment, the set accessor is invoked to assign a new value (Simple assignment). 在其他所有情況下,則會叫用 get 存取 子,以取得目前值 () 的運算式值In all other cases, the get accessor is invoked to obtain the current value (Values of expressions).

這種存取權This access

This_access 包含保留字 thisA this_access consists of the reserved word this.

this_access
    : 'this'
    ;

只有在實例的函式、實例方法或實例存取子的 區塊 中,才允許 this_accessA this_access is permitted only in the block of an instance constructor, an instance method, or an instance accessor. 它具有下列其中一種意義:It has one of the following meanings:

  • this 在類別的實例函式內的 primary_expression 中使用時,它會分類為值。When this is used in a primary_expression within an instance constructor of a class, it is classified as a value. 值的類型是實例類型, (使用發生所在之類別的 實例類型) ,而值則是所要建立之物件的參考。The type of the value is the instance type (The instance type) of the class within which the usage occurs, and the value is a reference to the object being constructed.
  • this 用於類別的實例方法或實例存取子內的 primary_expression 時,它會分類為值。When this is used in a primary_expression within an instance method or instance accessor of a class, it is classified as a value. 值的類型是實例類型, (使用發生所在之類別的 實例類型) ,而值則是對其叫用方法或存取子之物件的參考。The type of the value is the instance type (The instance type) of the class within which the usage occurs, and the value is a reference to the object for which the method or accessor was invoked.
  • this 在結構的實例函式內的 primary_expression 中使用時,它會分類為變數。When this is used in a primary_expression within an instance constructor of a struct, it is classified as a variable. 變數的類型是實例類型, (使用發生所在之結構的 實例類型) ,而變數代表所要建立的結構。The type of the variable is the instance type (The instance type) of the struct within which the usage occurs, and the variable represents the struct being constructed. this結構的實例函式的變數行為與結構類型的參數完全相同,特別是 out 這表示必須在實例的每個執行路徑中明確地指派變數。The this variable of an instance constructor of a struct behaves exactly the same as an out parameter of the struct type—in particular, this means that the variable must be definitely assigned in every execution path of the instance constructor.
  • this 在結構的實例方法或實例存取子內的 primary_expression 中使用時,它會分類為變數。When this is used in a primary_expression within an instance method or instance accessor of a struct, it is classified as a variable. 變數的類型是實例類型, (使用發生所在之結構的 實例類型) 。The type of the variable is the instance type (The instance type) of the struct within which the usage occurs.
    • 如果方法或存取子不是反覆運算器 (反覆運算 器) ,則 this 變數代表叫用方法或存取子的結構,而且行為與 ref 結構類型的參數完全相同。If the method or accessor is not an iterator (Iterators), the this variable represents the struct for which the method or accessor was invoked, and behaves exactly the same as a ref parameter of the struct type.
    • 如果方法或存取子是反覆運算器,則 this 變數代表已叫用方法或存取子之結構的複本,而且行為與結構類型的值參數完全相同。If the method or accessor is an iterator, the this variable represents a copy of the struct for which the method or accessor was invoked, and behaves exactly the same as a value parameter of the struct type.

thisprimary_expression 于上述內容以外的內容中使用,是編譯時期錯誤。Use of this in a primary_expression in a context other than the ones listed above is a compile-time error. 尤其是,無法 this 在靜態方法、靜態屬性存取子或欄位宣告的 variable_initializer 中參考。In particular, it is not possible to refer to this in a static method, a static property accessor, or in a variable_initializer of a field declaration.

基底存取Base access

Base_access 包含保留字, base 後面接著 " . " 權杖和識別碼或以方括弧括住的 argument_listA base_access consists of the reserved word base followed by either a "." token and an identifier or an argument_list enclosed in square brackets:

base_access
    : 'base' '.' identifier
    | 'base' '[' expression_list ']'
    ;

Base_access 是用來存取基類成員,這些成員在目前的類別或結構中以類似的命名成員隱藏。A base_access is used to access base class members that are hidden by similarly named members in the current class or struct. 只有在實例的函式、實例方法或實例存取子的 區塊 中,才允許 base_accessA base_access is permitted only in the block of an instance constructor, an instance method, or an instance accessor. base.I發生于類別或結構時, I 必須表示該類別或結構的基類成員。When base.I occurs in a class or struct, I must denote a member of the base class of that class or struct. 同樣地, base[E] 在類別中發生時,適用的索引子必須存在於基類中。Likewise, when base[E] occurs in a class, an applicable indexer must exist in the base class.

在系結時間,會將表單 base_access 運算式的 base.I base[E] 評估方式與撰寫時的完全 ((B)this).I 相同 ((B)this)[E] ,其中 B 是發生結構的類別或結構的基類。At binding-time, base_access expressions of the form base.I and base[E] are evaluated exactly as if they were written ((B)this).I and ((B)this)[E], where B is the base class of the class or struct in which the construct occurs. 因此, base.Ibase[E] 會對應至 this.Ithis[E] ,但 this 會被視為基類的實例。Thus, base.I and base[E] correspond to this.I and this[E], except this is viewed as an instance of the base class.

base_access 參考 (方法、屬性或索引子) 的虛擬函式成員時,會決定在執行時間時要叫用的函式成員 (編譯時間檢查動態 多載解析) 變更。When a base_access references a virtual function member (a method, property, or indexer), the determination of which function member to invoke at run-time (Compile-time checking of dynamic overload resolution) is changed. 所叫用的函式成員是藉由尋找最常衍生的實 (虛擬方法) 的函 B 式 (成員,而不是相對於的執行時間類型 this ,如同在非基底存取) 中一般。The function member that is invoked is determined by finding the most derived implementation (Virtual methods) of the function member with respect to B (instead of with respect to the run-time type of this, as would be usual in a non-base access). 因此,在 override virtual 函數成員的中, base_access 可以用來叫用繼承的函式成員實作為。Thus, within an override of a virtual function member, a base_access can be used to invoke the inherited implementation of the function member. 如果 base_access 所參考的函數成員是抽象的,則會發生系結階段錯誤。If the function member referenced by a base_access is abstract, a binding-time error occurs.

後置遞增和遞減運算子Postfix increment and decrement operators

post_increment_expression
    : primary_expression '++'
    ;

post_decrement_expression
    : primary_expression '--'
    ;

後置遞增或遞減運算的運算元必須是分類為變數、屬性存取或索引子存取的運算式。The operand of a postfix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. 運算的結果是與運算元相同類型的值。The result of the operation is a value of the same type as the operand.

如果 primary_expression 具有編譯時間類型 dynamic ,則運算子會動態系結 (動態繫結) 、 post_increment_expressionpost_decrement_expression 具有編譯時間類型, dynamic 而下列規則會在執行時間使用 primary_expression 的執行時間類型套用。If the primary_expression has the compile-time type dynamic then the operator is dynamically bound (Dynamic binding), the post_increment_expression or post_decrement_expression has the compile-time type dynamic and the following rules are applied at run-time using the run-time type of the primary_expression.

如果後置遞增或遞減運算的運算元是屬性或索引子存取,則屬性或索引子必須同時具有 get 和存取子 setIf the operand of a postfix increment or decrement operation is a property or indexer access, the property or indexer must have both a get and a set accessor. 如果不是這種情況,就會發生系結階段錯誤。If this is not the case, a binding-time error occurs.

一元運算子多載解析 (一元運算子 多載解析) 適用于選取特定的運算子執行。Unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 預先 ++ 定義 -- 的和運算子存在於下列類型:、、、、、、、、、、、 sbyte byte short ushort int uint long ulong char float double decimal 和任何列舉類型。Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. 預先定義的運算子會傳回將 ++ 1 加入運算元所產生的值,而預先定義的運算子會傳回 -- 從運算元減去1所產生的值。The predefined ++ operators return the value produced by adding 1 to the operand, and the predefined -- operators return the value produced by subtracting 1 from the operand. checked 內容中,如果這個加法或減法的結果超出結果型別的範圍,且結果型別是整數型別或列舉型別, System.OverflowException 就會擲回。In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a System.OverflowException is thrown.

表單的後置遞增或遞減作業的執行時間處理, x++ 或是 x-- 由下列步驟所組成:The run-time processing of a postfix increment or decrement operation of the form x++ or x-- consists of the following steps:

  • 如果 x 分類為變數:If x is classified as a variable:
    • x 會評估以產生變數。x is evaluated to produce the variable.
    • 的值 x 會儲存。The value of x is saved.
    • 使用已儲存的值 x 做為其引數來叫用選取的運算子。The selected operator is invoked with the saved value of x as its argument.
    • 運算子所傳回的值會儲存在評估所指定的位置 xThe value returned by the operator is stored in the location given by the evaluation of x.
    • 的儲存值 x 會成為作業的結果。The saved value of x becomes the result of the operation.
  • 如果 x 分類為屬性或索引子存取:If x is classified as a property or indexer access:
    • 如果不是) ,實例運算式 (x static ,且如果 x 是與相關聯的索引子存取) ,則會評估引數 (清單 x ,且結果會用於後續 getset 存取子調用。The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent get and set accessor invocations.
    • 會叫用的 get 存取子 x ,並儲存傳回的值。The get accessor of x is invoked and the returned value is saved.
    • 使用已儲存的值 x 做為其引數來叫用選取的運算子。The selected operator is invoked with the saved value of x as its argument.
    • set 存取子 x 會以運算子傳回的值做為其引數來叫用 valueThe set accessor of x is invoked with the value returned by the operator as its value argument.
    • 的儲存值 x 會成為作業的結果。The saved value of x becomes the result of the operation.

++And -- 運算子也支援前置詞標記法 (首碼遞增和遞減運算子) 。The ++ and -- operators also support prefix notation (Prefix increment and decrement operators). 或的結果通常是在作業 x++ x-- 之前的值 x ,而或的結果 ++x 是作業之後的 --xxTypically, the result of x++ or x-- is the value of x before the operation, whereas the result of ++x or --x is the value of x after the operation. 在任一種情況下, x 其本身在作業之後都具有相同的值。In either case, x itself has the same value after the operation.

operator ++ operator -- 可以使用後置或前置標記法來叫用或實作為。An operator ++ or operator -- implementation can be invoked using either postfix or prefix notation. 這兩種標記法不能有個別的運算子實現。It is not possible to have separate operator implementations for the two notations.

new 運算子The new operator

new運算子用來建立類型的新實例。The new operator is used to create new instances of types.

有三種形式的 new 運算式:There are three forms of new expressions:

  • 物件建立運算式可用來建立類別型別和實數值型別的新實例。Object creation expressions are used to create new instances of class types and value types.
  • 陣列建立運算式可用來建立陣列類型的新實例。Array creation expressions are used to create new instances of array types.
  • 委派建立運算式是用來建立委派類型的新實例。Delegate creation expressions are used to create new instances of delegate types.

new運算子意指建立型別的實例,但不一定表示動態配置記憶體。The new operator implies creation of an instance of a type, but does not necessarily imply dynamic allocation of memory. 尤其是實值型別的實例不需要額外的記憶體,就是它們所在的變數,而且當 new 用來建立實值型別的實例時,不會發生任何動態配置。In particular, instances of value types require no additional memory beyond the variables in which they reside, and no dynamic allocations occur when new is used to create instances of value types.

物件建立運算式Object creation expressions

Object_creation_expression 用來建立 class_typevalue_type 的新實例。An object_creation_expression is used to create a new instance of a class_type or a value_type.

object_creation_expression
    : 'new' type '(' argument_list? ')' object_or_collection_initializer?
    | 'new' type object_or_collection_initializer
    ;

object_or_collection_initializer
    : object_initializer
    | collection_initializer
    ;

Object_creation_expression 別必須是 class_typevalue_typetype_parameterThe type of an object_creation_expression must be a class_type, a value_type or a type_parameter. 類型 不能是 abstract class_typeThe type cannot be an abstract class_type.

只有當 類型class_typestruct_type 時,才允許選擇性的 argument_list (引數清單) 。The optional argument_list (Argument lists) is permitted only if the type is a class_type or a struct_type.

物件建立運算式可以省略函式引數清單,並在包含物件初始化運算式或集合初始化運算式時,括住括弧。An object creation expression can omit the constructor argument list and enclosing parentheses provided it includes an object initializer or collection initializer. 省略函式引數清單和封閉括弧相當於指定空的引數清單。Omitting the constructor argument list and enclosing parentheses is equivalent to specifying an empty argument list.

處理包含物件初始化運算式或集合初始化運算式的物件建立運算式包含第一次處理實例的函式,然後處理物件初始化運算式所指定的成員或元素初始化 (物件 初始化運算式) 或集合初始化運算式 (集合初始化 運算式) 。Processing of an object creation expression that includes an object initializer or collection initializer consists of first processing the instance constructor and then processing the member or element initializations specified by the object initializer (Object initializers) or collection initializer (Collection initializers).

如果選擇性 argument_list 中的任何引數具有編譯時間類型,則 dynamic object_creation_expression 會動態系結 (動態繫結) ,而且在執行時間會使用具有編譯時間類型之 argument_list 引數的執行時間類型,套用下列規則 dynamicIf any of the arguments in the optional argument_list has the compile-time type dynamic then the object_creation_expression is dynamically bound (Dynamic binding) and the following rules are applied at run-time using the run-time type of those arguments of the argument_list that have the compile time type dynamic. 不過,建立物件時,會依照動態多載 解析的編譯時間檢查所述,進行有限的編譯時間檢查。However, the object creation undergoes a limited compile time check as described in Compile-time checking of dynamic overload resolution.

表單 object_creation_expression 的系結時間處理 new T(A) ,其中 Tclass_typevalue_type ,而 A 是選擇性的 argument_list,包含下列步驟:The binding-time processing of an object_creation_expression of the form new T(A), where T is a class_type or a value_type and A is an optional argument_list, consists of the following steps:

  • 如果 Tvalue_typeA 不存在:If T is a value_type and A is not present:
    • Object_creation_expression 是預設的函式呼叫。The object_creation_expression is a default constructor invocation. Object_creation_expression 的結果是類型的值,也就是在 T T system.object 類型中定義的預設值。The result of the object_creation_expression is a value of type T, namely the default value for T as defined in The System.ValueType type.
  • 否則,如果 Ttype_parameter 且不 A 存在:Otherwise, if T is a type_parameter and A is not present:
    • 如果未指定任何實值型別條件約束或函式條件約束 (類型參數條件 約束) T ,就會發生系結階段錯誤。If no value type constraint or constructor constraint (Type parameter constraints) has been specified for T, a binding-time error occurs.
    • Object_creation_expression 的結果是型別參數已系結之執行時間類型的值,也就是叫用該型別的預設函式的結果。The result of the object_creation_expression is a value of the run-time type that the type parameter has been bound to, namely the result of invoking the default constructor of that type. 執行時間類型可以是參考型別或實值型別。The run-time type may be a reference type or a value type.
  • 否則,如果 Tclass_typestruct_typeOtherwise, if T is a class_type or a struct_type:
    • 如果 Tabstract class_type,就會發生編譯時期錯誤。If T is an abstract class_type, a compile-time error occurs.
    • 要叫用的實例函式是使用多載解析規則的多載解析規則來決定。The instance constructor to invoke is determined using the overload resolution rules of Overload resolution. 這組候選實例的函式是由中宣告的所有可存取的實例函式所組成, T 這些都適用于 A (適用的函數成員) 。The set of candidate instance constructors consists of all accessible instance constructors declared in T which are applicable with respect to A (Applicable function member). 如果候選實例的函式集合是空的,或無法識別單一最佳實例的函式,則會發生系結階段錯誤。If the set of candidate instance constructors is empty, or if a single best instance constructor cannot be identified, a binding-time error occurs.
    • Object_creation_expression 的結果是類型的值,也就是叫用 T 上述步驟中所決定的實例函式所產生的值。The result of the object_creation_expression is a value of type T, namely the value produced by invoking the instance constructor determined in the step above.
  • 否則, object_creation_expression 無效,而且會發生系結階段錯誤。Otherwise, the object_creation_expression is invalid, and a binding-time error occurs.

即使 object_creation_expression 動態繫結,編譯時間類型仍是 TEven if the object_creation_expression is dynamically bound, the compile-time type is still T.

表單 object_creation_expression 的執行時間處理 new T(A) ,其中 Tclass_typestruct_type ,而 A 是選擇性的 argument_list,其中包含下列步驟:The run-time processing of an object_creation_expression of the form new T(A), where T is class_type or a struct_type and A is an optional argument_list, consists of the following steps:

  • 如果 Tclass_typeIf T is a class_type:
    • 配置給類別的新實例 TA new instance of class T is allocated. 如果沒有足夠的記憶體可配置新的實例,則會擲回, System.OutOfMemoryException 且不會執行任何進一步的步驟。If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
    • 新實例的所有欄位都會初始化為預設值 (預設值) 。All fields of the new instance are initialized to their default values (Default values).
    • 根據函數成員調用的規則叫用實例的函式, (動態多載解析) 的編譯時期檢查The instance constructor is invoked according to the rules of function member invocation (Compile-time checking of dynamic overload resolution). 新配置之實例的參考會自動傳遞給實例的函式,而且可以在該函式內以形式存取實例 thisA reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this.
  • 如果 Tstruct_typeIf T is a struct_type:
    • 型別的實例 T 是藉由配置暫存區域變數所建立。An instance of type T is created by allocating a temporary local variable. 由於 struct_type 的實例函式必須將值明確指派給要建立之實例的每個欄位,因此不需要初始化暫存變數。Since an instance constructor of a struct_type is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary.
    • 根據函數成員調用的規則叫用實例的函式, (動態多載解析) 的編譯時期檢查The instance constructor is invoked according to the rules of function member invocation (Compile-time checking of dynamic overload resolution). 新配置之實例的參考會自動傳遞給實例的函式,而且可以在該函式內以形式存取實例 thisA reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this.

物件初始設定式Object initializers

物件初始化運算式 會為物件的零或多個欄位、屬性或索引元素指定值。An object initializer specifies values for zero or more fields, properties or indexed elements of an object.

object_initializer
    : '{' member_initializer_list? '}'
    | '{' member_initializer_list ',' '}'
    ;

member_initializer_list
    : member_initializer (',' member_initializer)*
    ;

member_initializer
    : initializer_target '=' initializer_value
    ;

initializer_target
    : identifier
    | '[' argument_list ']'
    ;

initializer_value
    : expression
    | object_or_collection_initializer
    ;

物件初始化運算式是由一系列的成員初始化運算式所組成,並以和標記括住, { } 並以逗號分隔。An object initializer consists of a sequence of member initializers, enclosed by { and } tokens and separated by commas. 每個 member_initializer 都會指定初始化的目標。Each member_initializer designates a target for the initialization. 識別碼 必須命名要初始化之物件的可存取欄位或屬性,而以方括弧括住的 argument_list 必須指定要初始化之物件上的可存取索引子引數。An identifier must name an accessible field or property of the object being initialized, whereas an argument_list enclosed in square brackets must specify arguments for an accessible indexer on the object being initialized. 物件初始化運算式針對相同的欄位或屬性包含一個以上的成員初始化運算式時,會發生錯誤。It is an error for an object initializer to include more than one member initializer for the same field or property.

每個 initializer_target 後面都會加上等號,以及運算式、物件初始化運算式或集合初始化運算式。Each initializer_target is followed by an equals sign and either an expression, an object initializer or a collection initializer. 物件初始化運算式內的運算式無法參考其正在初始化的新建立的物件。It is not possible for expressions within the object initializer to refer to the newly created object it is initializing.

成員初始化運算式,指定等號之後的運算式會以與指派 (簡單指派) 的相同方式處理。A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (Simple assignment) to the target.

成員初始化運算式,指定等號後面的物件初始化運算式是 嵌套物件初始化運算式,亦即内嵌物件的初始化。A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. 將嵌套物件初始化運算式中的指派視為欄位或屬性成員的指派,而不是指派新值給欄位或屬性。Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. 您無法將嵌套物件初始化運算式套用至具有實數值型別的屬性,也無法套用至具有實數值型別的唯讀欄位。Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.

成員初始化運算式,會在等號之後指定集合初始化運算式為內嵌集合的初始化。A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. 它不會將新的集合指派給目標欄位、屬性或索引子,而是會將初始化運算式中提供的專案加入至目標所參考的集合。Instead of assigning a new collection to the target field, property or indexer, the elements given in the initializer are added to the collection referenced by the target. 目標的集合類型必須符合 集合初始化運算式中所指定的需求。The target must be of a collection type that satisfies the requirements specified in Collection initializers.

索引初始化運算式的引數一律只會評估一次。The arguments to an index initializer will always be evaluated exactly once. 因此,即使引數最後不會使用 (例如,因為有空白的嵌套初始化運算式) ,所以會評估它們是否有副作用。Thus, even if the arguments end up never getting used (e.g. because of an empty nested initializer), they will be evaluated for their side effects.

下列類別代表具有兩個座標的點:The following class represents a point with two coordinates:

public class Point
{
    int x, y;

    public int X { get { return x; } set { x = value; } }
    public int Y { get { return y; } set { y = value; } }
}

的實例 Point 可以建立和初始化,如下所示:An instance of Point can be created and initialized as follows:

Point a = new Point { X = 0, Y = 1 };

其效果與which has the same effect as

Point __a = new Point();
__a.X = 0;
__a.Y = 1; 
Point a = __a;

其中 __a 是其他隱藏且無法存取的暫存變數。where __a is an otherwise invisible and inaccessible temporary variable. 下列類別代表從兩個點建立的矩形:The following class represents a rectangle created from two points:

public class Rectangle
{
    Point p1, p2;

    public Point P1 { get { return p1; } set { p1 = value; } }
    public Point P2 { get { return p2; } set { p2 = value; } }
}

的實例 Rectangle 可以建立和初始化,如下所示:An instance of Rectangle can be created and initialized as follows:

Rectangle r = new Rectangle {
    P1 = new Point { X = 0, Y = 1 },
    P2 = new Point { X = 2, Y = 3 }
};

其效果與which has the same effect as

Rectangle __r = new Rectangle();
Point __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
__r.P1 = __p1;
Point __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
__r.P2 = __p2; 
Rectangle r = __r;

其中 __r__p1__p2 是不可見和無法存取的臨時變數。where __r, __p1 and __p2 are temporary variables that are otherwise invisible and inaccessible.

If 的函式會配置 Rectangle 兩個內嵌的 Point 實例If Rectangle's constructor allocates the two embedded Point instances

public class Rectangle
{
    Point p1 = new Point();
    Point p2 = new Point();

    public Point P1 { get { return p1; } }
    public Point P2 { get { return p2; } }
}

下列結構可以用來初始化內嵌的實例, Point 而不是指派新的實例:the following construct can be used to initialize the embedded Point instances instead of assigning new instances:

Rectangle r = new Rectangle {
    P1 = { X = 0, Y = 1 },
    P2 = { X = 2, Y = 3 }
};

其效果與which has the same effect as

Rectangle __r = new Rectangle();
__r.P1.X = 0;
__r.P1.Y = 1;
__r.P2.X = 2;
__r.P2.Y = 3;
Rectangle r = __r;

假設有適當的 C 定義,則下列範例:Given an appropriate definition of C, the following example:

var c = new C {
    x = true,
    y = { a = "Hello" },
    z = { 1, 2, 3 },
    ["x"] = 5,
    [0,0] = { "a", "b" },
    [1,2] = {}
};

相當於這一系列的指派:is equivalent to this series of assignments:

C __c = new C();
__c.x = true;
__c.y.a = "Hello";
__c.z.Add(1); 
__c.z.Add(2);
__c.z.Add(3);
string __i1 = "x";
__c[__i1] = 5;
int __i2 = 0, __i3 = 0;
__c[__i2,__i3].Add("a");
__c[__i2,__i3].Add("b");
int __i4 = 1, __i5 = 2;
var c = __c;

其中 __c ,會產生不可見且無法存取原始程式碼的變數。where __c, etc., are generated variables that are invisible and inaccessible to the source code. 請注意,的引數 [0,0] 只會評估一次,而的引數會 [1,2] 評估一次,即使從未使用過也一樣。Note that the arguments for [0,0] are evaluated only once, and the arguments for [1,2] are evaluated once even though they are never used.

集合初始設定式Collection initializers

集合初始化運算式會指定集合的元素。A collection initializer specifies the elements of a collection.

collection_initializer
    : '{' element_initializer_list '}'
    | '{' element_initializer_list ',' '}'
    ;

element_initializer_list
    : element_initializer (',' element_initializer)*
    ;

element_initializer
    : non_assignment_expression
    | '{' expression_list '}'
    ;

expression_list
    : expression (',' expression)*
    ;

集合初始化運算式是由一系列的元素初始化運算式所組成,並以和標記括住, { } 並以逗號分隔。A collection initializer consists of a sequence of element initializers, enclosed by { and } tokens and separated by commas. 每個專案初始化運算式會指定要加入至要初始化之集合物件的專案,並包含以和標記括住的運算式清單, { } 並以逗號分隔。Each element initializer specifies an element to be added to the collection object being initialized, and consists of a list of expressions enclosed by { and } tokens and separated by commas. 單一運算式專案初始化運算式可以不使用大括弧來撰寫,但之後也不能是指派運算式,以避免與成員初始化運算式混淆。A single-expression element initializer can be written without braces, but cannot then be an assignment expression, to avoid ambiguity with member initializers. Non_assignment_expression 生產是在 expression中定義。The non_assignment_expression production is defined in Expression.

以下是包含集合初始化運算式的物件建立運算式範例:The following is an example of an object creation expression that includes a collection initializer:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

要套用集合初始化運算式的集合物件,必須是可執行檔型別, System.Collections.IEnumerable 或發生編譯時期錯誤。The collection object to which a collection initializer is applied must be of a type that implements System.Collections.IEnumerable or a compile-time error occurs. 集合初始化運算式會針對每個指定的專案, Add 使用專案初始化運算式的運算式清單做為引數清單,在目標物件上叫用方法,並為每個調用套用一般成員查閱和多載解析。For each specified element in order, the collection initializer invokes an Add method on the target object with the expression list of the element initializer as argument list, applying normal member lookup and overload resolution for each invocation. 因此,collection 物件必須有適用的實例或擴充方法,且 Add 每個專案初始化運算式都有名稱。Thus, the collection object must have an applicable instance or extension method with the name Add for each element initializer.

下列類別代表具有名稱和電話號碼清單的連絡人:The following class represents a contact with a name and a list of phone numbers:

public class Contact
{
    string name;
    List<string> phoneNumbers = new List<string>();

    public string Name { get { return name; } set { name = value; } }

    public List<string> PhoneNumbers { get { return phoneNumbers; } }
}

List<Contact>可以建立和初始化,如下所示:A List<Contact> can be created and initialized as follows:

var contacts = new List<Contact> {
    new Contact {
        Name = "Chris Smith",
        PhoneNumbers = { "206-555-0101", "425-882-8080" }
    },
    new Contact {
        Name = "Bob Harris",
        PhoneNumbers = { "650-555-0199" }
    }
};

其效果與which has the same effect as

var __clist = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
__clist.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
__clist.Add(__c2);
var contacts = __clist;

其中 __clist__c1__c2 是不可見和無法存取的臨時變數。where __clist, __c1 and __c2 are temporary variables that are otherwise invisible and inaccessible.

陣列建立運算式Array creation expressions

Array_creation_expression 用來建立 array_type 的新實例。An array_creation_expression is used to create a new instance of an array_type.

array_creation_expression
    : 'new' non_array_type '[' expression_list ']' rank_specifier* array_initializer?
    | 'new' array_type array_initializer
    | 'new' rank_specifier array_initializer
    ;

第一個表單的陣列建立運算式會配置從運算式清單中刪除每個個別運算式所產生之類型的陣列實例。An array creation expression of the first form allocates an array instance of the type that results from deleting each of the individual expressions from the expression list. 例如,陣列建立運算式會 new int[10,20] 產生型別的陣列實例 int[,] ,而陣列建立運算式會 new int[10][,] 產生型別的陣列 int[][,]For example, the array creation expression new int[10,20] produces an array instance of type int[,], and the array creation expression new int[10][,] produces an array of type int[][,]. 運算式清單中的每個運算式都必須是 intuintlong 或類型,或是可 ulong 隱含轉換成這些類型的一或多個。Each expression in the expression list must be of type int, uint, long, or ulong, or implicitly convertible to one or more of these types. 每個運算式的值會決定新配置陣列實例中對應維度的長度。The value of each expression determines the length of the corresponding dimension in the newly allocated array instance. 因為陣列維度的長度必須是非負值,所以在運算式清單中有一個具有負值的 constant_expression 是編譯時期錯誤。Since the length of an array dimension must be nonnegative, it is a compile-time error to have a constant_expression with a negative value in the expression list.

除了在 unsafe 內容中 (不安全 的內容) 之外,不會指定陣列的配置。Except in an unsafe context (Unsafe contexts), the layout of arrays is unspecified.

如果第一個表單的陣列建立運算式包含陣列初始化運算式,則運算式清單中的每個運算式都必須是常數,而且運算式清單所指定的順位和維度長度必須符合陣列初始化運算式的運算式。If an array creation expression of the first form includes an array initializer, each expression in the expression list must be a constant and the rank and dimension lengths specified by the expression list must match those of the array initializer.

在第二或第三個表單的陣列建立運算式中,指定之陣列類型或等級規範的等級必須符合陣列初始化運算式的次序。In an array creation expression of the second or third form, the rank of the specified array type or rank specifier must match that of the array initializer. 個別的維度長度是從陣列初始化運算式的每個對應嵌套層級中的元素數目推斷而來。The individual dimension lengths are inferred from the number of elements in each of the corresponding nesting levels of the array initializer. 因此,運算式Thus, the expression

new int[,] {{0, 1}, {2, 3}, {4, 5}}

完全對應至exactly corresponds to

new int[3, 2] {{0, 1}, {2, 3}, {4, 5}}

第三個表單的陣列建立運算式稱為「隱含類型陣列建立運算式 _」。An array creation expression of the third form is referred to as an *implicitly typed array creation expression _. 它類似于第二個表單,不同之處在于陣列的元素類型未明確指定,但卻決定為最佳的一般型別, (在陣列初始化運算式中尋找一組運算式) 的 最佳一般 類型。It is similar to the second form, except that the element type of the array is not explicitly given, but determined as the best common type (Finding the best common type of a set of expressions) of the set of expressions in the array initializer. 若為多維度陣列,也就是 _rank_specifier * 至少包含一個逗號的運算式,此集合會包含在嵌套 array_initializer s 中找到的所有 運算式For a multidimensional array, i.e., one where the _rank_specifier* contains at least one comma, this set comprises all expression s found in nested array_initializer s.

陣列初始化運算式會在 陣列初始化運算式中進一步說明。Array initializers are described further in Array initializers.

評估陣列建立運算式的結果會分類為值,亦即新配置陣列實例的參考。The result of evaluating an array creation expression is classified as a value, namely a reference to the newly allocated array instance. 陣列建立運算式的執行時間處理是由下列步驟所組成:The run-time processing of an array creation expression consists of the following steps:

  • Expression_list 的維度長度運算式會依序從左至右進行評估。The dimension length expressions of the expression_list are evaluated in order, from left to right. 在評估每個運算式之後,會執行隱含轉換 (隱含 轉換) 為下列其中一種類型: intuintlongulongFollowing evaluation of each expression, an implicit conversion (Implicit conversions) to one of the following types is performed: int, uint, long, ulong. 此清單中的第一個類型會選擇隱含轉換存在。The first type in this list for which an implicit conversion exists is chosen. 如果運算式的評估或後續的隱含轉換造成例外狀況,則不會評估任何進一步的運算式,也不會執行任何進一步的步驟。If evaluation of an expression or the subsequent implicit conversion causes an exception, then no further expressions are evaluated and no further steps are executed.
  • 維度長度的計算值會依照下列方式進行驗證。The computed values for the dimension lengths are validated as follows. 如果有一或多個值小於零,則會擲回, System.OverflowException 且不會執行任何進一步的步驟。If one or more of the values are less than zero, a System.OverflowException is thrown and no further steps are executed.
  • 配置具有給定維度長度的陣列實例。An array instance with the given dimension lengths is allocated. 如果沒有足夠的記憶體可配置新的實例,則會擲回, System.OutOfMemoryException 且不會執行任何進一步的步驟。If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
  • 新陣列實例的所有元素都會初始化為預設值 (預設值) 。All elements of the new array instance are initialized to their default values (Default values).
  • 如果陣列建立運算式包含陣列初始化運算式,則會評估陣列初始化運算式中的每個運算式,並將其指派給其對應的陣列元素。If the array creation expression contains an array initializer, then each expression in the array initializer is evaluated and assigned to its corresponding array element. 評估和指派會依照運算式在陣列初始化運算式中的寫入順序來執行,換句話說,專案會以遞增的索引順序初始化,而最右邊的維度會先遞增。The evaluations and assignments are performed in the order the expressions are written in the array initializer—in other words, elements are initialized in increasing index order, with the rightmost dimension increasing first. 如果指定運算式的評估或對應的陣列元素的後續指派造成例外狀況,則不會 (進一步的元素初始化,其餘的元素則會) 其預設值。If evaluation of a given expression or the subsequent assignment to the corresponding array element causes an exception, then no further elements are initialized (and the remaining elements will thus have their default values).

陣列建立運算式允許具現化陣列型別元素的陣列,但是這類陣列的元素必須以手動方式初始化。An array creation expression permits instantiation of an array with elements of an array type, but the elements of such an array must be manually initialized. 例如,語句For example, the statement

int[][] a = new int[100][];

建立具有類型100元素的一維陣列 int[]creates a single-dimensional array with 100 elements of type int[]. 每個元素的初始值為 nullThe initial value of each element is null. 相同的陣列建立運算式也不可能同時具現化子陣列和語句It is not possible for the same array creation expression to also instantiate the sub-arrays, and the statement

int[][] a = new int[100][5];        // Error

導致編譯階段錯誤。results in a compile-time error. 子陣列的具現化必須改為手動執行,如下所示:Instantiation of the sub-arrays must instead be performed manually, as in

int[][] a = new int[100][];
for (int i = 0; i < 100; i++) a[i] = new int[5];

當陣列陣列有「矩形」圖形時,也就是當子陣列的長度都相同時,使用多維度陣列會更有效率。When an array of arrays has a "rectangular" shape, that is when the sub-arrays are all of the same length, it is more efficient to use a multi-dimensional array. 在上述範例中,陣列陣列的具現化會建立101物件—一種外部陣列和100子陣列。In the example above, instantiation of the array of arrays creates 101 objects—one outer array and 100 sub-arrays. 相反地,In contrast,

int[,] = new int[100, 5];

只建立單一物件、二維陣列,並在單一語句中完成配置。creates only a single object, a two-dimensional array, and accomplishes the allocation in a single statement.

以下是隱含類型陣列建立運算式的範例:The following are examples of implicitly typed array creation expressions:

var a = new[] { 1, 10, 100, 1000 };                       // int[]

var b = new[] { 1, 1.5, 2, 2.5 };                         // double[]

var c = new[,] { { "hello", null }, { "world", "!" } };   // string[,]

var d = new[] { 1, "one", 2, "two" };                     // Error

最後一個運算式會導致編譯時期錯誤,因為兩者都不能 int string 隱含地轉換成另一個,所以沒有最佳的一般類型。The last expression causes a compile-time error because neither int nor string is implicitly convertible to the other, and so there is no best common type. 在此情況下,必須使用明確類型的陣列建立運算式,例如將型別指定為 object[]An explicitly typed array creation expression must be used in this case, for example specifying the type to be object[]. 或者,您可以將其中一個專案轉換成通用基底類型,然後再成為推斷的元素類型。Alternatively, one of the elements can be cast to a common base type, which would then become the inferred element type.

隱含型別陣列建立運算式可以與匿名物件初始化運算式結合 (匿名物件建立運算式 ,) 建立匿名型別的資料結構。Implicitly typed array creation expressions can be combined with anonymous object initializers (Anonymous object creation expressions) to create anonymously typed data structures. 例如:For example:

var contacts = new[] {
    new {
        Name = "Chris Smith",
        PhoneNumbers = new[] { "206-555-0101", "425-882-8080" }
    },
    new {
        Name = "Bob Harris",
        PhoneNumbers = new[] { "650-555-0199" }
    }
};

委派建立運算式Delegate creation expressions

Delegate_creation_expression 用來建立 delegate_type 的新實例。A delegate_creation_expression is used to create a new instance of a delegate_type.

delegate_creation_expression
    : 'new' delegate_type '(' expression ')'
    ;

委派建立運算式的引數必須是方法群組、匿名函數或編譯時間類型 dynamicdelegate_type 的值。The argument of a delegate creation expression must be a method group, an anonymous function or a value of either the compile time type dynamic or a delegate_type. 如果引數是方法群組,它會識別方法,並針對實例方法識別要建立委派的物件。If the argument is a method group, it identifies the method and, for an instance method, the object for which to create a delegate. 如果引數是匿名函式,它會直接定義委派目標的參數和方法主體。If the argument is an anonymous function it directly defines the parameters and method body of the delegate target. 如果引數是值,它會識別要建立複本的委派實例。If the argument is a value it identifies a delegate instance of which to create a copy.

如果 運算式 具有編譯時間類型 dynamic ,則 delegate_creation_expression 會動態系結 (動態繫結) ,且下列規則會在執行時間使用 運算式 的執行時間類型來套用。If the expression has the compile-time type dynamic, the delegate_creation_expression is dynamically bound (Dynamic binding), and the rules below are applied at run-time using the run-time type of the expression. 否則會在編譯時期套用規則。Otherwise the rules are applied at compile-time.

表單 delegate_creation_expression 的系結時間處理 new D(E) (其中 Ddelegate_type ,而 E運算式),包含下列步驟:The binding-time processing of a delegate_creation_expression of the form new D(E), where D is a delegate_type and E is an expression, consists of the following steps:

  • 如果 E 是方法群組,則會使用與方法群組轉換相同的方式來處理委派建立運算式 (方法群組 轉換) 從轉換 EDIf E is a method group, the delegate creation expression is processed in the same way as a method group conversion (Method group conversions) from E to D.
  • 如果 E 是匿名函式,則會使用與匿名函式轉換相同的方式來處理委派建立運算式 (從) 到的 匿名 函式轉換 E DIf E is an anonymous function, the delegate creation expression is processed in the same way as an anonymous function conversion (Anonymous function conversions) from E to D.
  • 如果 E 是值,就 E 必須與) 的 委派 宣告 (相容, D 而且結果會參考與 D 相同的調用清單所建立之新建立的委派型別 EIf E is a value, E must be compatible (Delegate declarations) with D, and the result is a reference to a newly created delegate of type D that refers to the same invocation list as E. 如果 E 與不相容 D ,就會發生編譯時期錯誤。If E is not compatible with D, a compile-time error occurs.

表單 delegate_creation_expression 的執行時間處理 new D(E) (其中 Ddelegate_type ,而 E運算式),包含下列步驟:The run-time processing of a delegate_creation_expression of the form new D(E), where D is a delegate_type and E is an expression, consists of the following steps:

  • 如果 E 是方法群組,則會將委派建立運算式評估為方法群組轉換, (從) 至的 方法群組 轉換 E DIf E is a method group, the delegate creation expression is evaluated as a method group conversion (Method group conversions) from E to D.
  • 如果 E 是匿名函式,則會將委派建立評估為的匿名函式轉換, E D) (匿名 函數轉換。If E is an anonymous function, the delegate creation is evaluated as an anonymous function conversion from E to D (Anonymous function conversions).
  • 如果 Edelegate_type 的值:If E is a value of a delegate_type:
    • E 會進行評估。E is evaluated. 如果此評估造成例外狀況,則不會執行任何進一步的步驟。If this evaluation causes an exception, no further steps are executed.
    • 如果的值 Enull ,則會擲回, System.NullReferenceException 且不會執行任何進一步的步驟。If the value of E is null, a System.NullReferenceException is thrown and no further steps are executed.
    • 配置委派類型的新實例 DA new instance of the delegate type D is allocated. 如果沒有足夠的記憶體可配置新的實例,則會擲回, System.OutOfMemoryException 且不會執行任何進一步的步驟。If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
    • 新的委派實例初始化時,所使用的調用清單與提供的委派實例相同 EThe new delegate instance is initialized with the same invocation list as the delegate instance given by E.

委派的調用清單是在委派具現化時決定的,然後在委派的整個存留期維持不變。The invocation list of a delegate is determined when the delegate is instantiated and then remains constant for the entire lifetime of the delegate. 換句話說,建立委派之後,就無法變更該委派的目標可呼叫實體。In other words, it is not possible to change the target callable entities of a delegate once it has been created. 當結合兩個委派,或從另一個 (委派 宣告中移除一個委派時) ,會有一個新的委派結果;沒有任何現有的委派已變更其內容。When two delegates are combined or one is removed from another (Delegate declarations), a new delegate results; no existing delegate has its contents changed.

您無法建立參考屬性、索引子、使用者定義的運算子、實例的函式、函式或靜態函式的委派。It is not possible to create a delegate that refers to a property, indexer, user-defined operator, instance constructor, destructor, or static constructor.

如上所述,從方法群組建立委派時,委派的型式參數清單和傳回型別會決定要選取的多載方法。As described above, when a delegate is created from a method group, the formal parameter list and return type of the delegate determine which of the overloaded methods to select. 在範例中In the example

delegate double DoubleFunc(double x);

class A
{
    DoubleFunc f = new DoubleFunc(Square);

    static float Square(float x) {
        return x * x;
    }

    static double Square(double x) {
        return x * x;
    }
}

A.f 欄位會使用參考第二個方法的委派來初始化, Square 因為該方法完全符合的型式參數清單和傳回型別 DoubleFuncthe A.f field is initialized with a delegate that refers to the second Square method because that method exactly matches the formal parameter list and return type of DoubleFunc. 如果有第二個 Square 方法不存在,就會發生編譯時期錯誤。Had the second Square method not been present, a compile-time error would have occurred.

匿名物件建立運算式Anonymous object creation expressions

Anonymous_object_creation_expression 用來建立匿名型別的物件。An anonymous_object_creation_expression is used to create an object of an anonymous type.

anonymous_object_creation_expression
    : 'new' anonymous_object_initializer
    ;

anonymous_object_initializer
    : '{' member_declarator_list? '}'
    | '{' member_declarator_list ',' '}'
    ;

member_declarator_list
    : member_declarator (',' member_declarator)*
    ;

member_declarator
    : simple_name
    | member_access
    | base_access
    | null_conditional_member_access
    | identifier '=' expression
    ;

匿名物件初始化運算式會宣告匿名型別,並傳回該型別的實例。An anonymous object initializer declares an anonymous type and returns an instance of that type. 匿名型別是直接繼承自的無型別的類別型別 objectAn anonymous type is a nameless class type that inherits directly from object. 匿名型別的成員是從匿名物件初始化運算式(用來建立類型的實例)推斷而來的唯讀屬性序列。The members of an anonymous type are a sequence of read-only properties inferred from the anonymous object initializer used to create an instance of the type. 具體而言,就是表單的匿名物件初始化運算式Specifically, an anonymous object initializer of the form

new { p1 = e1, p2 = e2, ..., pn = en }

宣告格式的匿名型別declares an anonymous type of the form

class __Anonymous1
{
    private readonly T1 f1;
    private readonly T2 f2;
    ...
    private readonly Tn fn;

    public __Anonymous1(T1 a1, T2 a2, ..., Tn an) {
        f1 = a1;
        f2 = a2;
        ...
        fn = an;
    }

    public T1 p1 { get { return f1; } }
    public T2 p2 { get { return f2; } }
    ...
    public Tn pn { get { return fn; } }

    public override bool Equals(object __o) { ... }
    public override int GetHashCode() { ... }
}

其中每個 Tx 都是對應運算式的類型 exwhere each Tx is the type of the corresponding expression ex. Member_declarator 中使用的運算式必須有型別。The expression used in a member_declarator must have a type. 因此, member_declarator 中的運算式為 null 或匿名函式時,就會發生編譯時期錯誤。Thus, it is a compile-time error for an expression in a member_declarator to be null or an anonymous function. 它也是一個編譯時期錯誤,讓運算式具有不安全的型別。It is also a compile-time error for the expression to have an unsafe type.

編譯器會自動產生匿名型別的名稱以及其方法的參數, Equals 而且不能在程式文字中參考。The names of an anonymous type and of the parameter to its Equals method are automatically generated by the compiler and cannot be referenced in program text.

在相同的程式中,兩個以相同順序指定相同名稱和編譯時間類型之屬性順序的匿名物件初始化運算式,將會產生相同匿名型別的實例。Within the same program, two anonymous object initializers that specify a sequence of properties of the same names and compile-time types in the same order will produce instances of the same anonymous type.

在範例中In the example

var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;

因為 p1p2 是相同的匿名型別,所以允許最後一行上的指派。the assignment on the last line is permitted because p1 and p2 are of the same anonymous type.

Equals GetHashcode 匿名型別上的和方法會覆寫繼承自的方法, object 並且會根據屬性的和來定義 Equals GetHashcode ,如此一來,只有在所有屬性都相等時,相同匿名型別的兩個實例才會相等。The Equals and GetHashcode methods on anonymous types override the methods inherited from object, and are defined in terms of the Equals and GetHashcode of the properties, so that two instances of the same anonymous type are equal if and only if all their properties are equal.

成員宣告子可以縮寫為簡單名稱 (類型推斷) 、成員存取 (動態多載 解析) 的編譯時期檢查 、基底存取 (基底存取) ,或 null 條件成員存取 (null 條件運算式作為投影初始化運算式) 。A member declarator can be abbreviated to a simple name (Type inference), a member access (Compile-time checking of dynamic overload resolution), a base access (Base access) or a null-conditional member access (Null-conditional expressions as projection initializers). 這稱為 投射初始化運算式 ,而且是的宣告和指派給相同名稱之屬性的速記。This is called a projection initializer and is shorthand for a declaration of and assignment to a property with the same name. 具體而言,表單的成員宣告子Specifically, member declarators of the forms

identifier
expr.identifier

分別相當於下列各項:are precisely equivalent to the following, respectively:

identifier = identifier
identifier = expr.identifier

因此,在投射初始化運算式中, 識別碼 會同時選取值以及指派值的欄位或屬性。Thus, in a projection initializer the identifier selects both the value and the field or property to which the value is assigned. 直覺來說,投射初始化運算式專案不只是值,也是值的名稱。Intuitively, a projection initializer projects not just a value, but also the name of the value.

Typeof 運算子The typeof operator

typeof運算子用來取得 System.Type 類型的物件。The typeof operator is used to obtain the System.Type object for a type.

typeof_expression
    : 'typeof' '(' type ')'
    | 'typeof' '(' unbound_type_name ')'
    | 'typeof' '(' 'void' ')'
    ;

unbound_type_name
    : identifier generic_dimension_specifier?
    | identifier '::' identifier generic_dimension_specifier?
    | unbound_type_name '.' identifier generic_dimension_specifier?
    ;

generic_dimension_specifier
    : '<' comma* '>'
    ;

comma
    : ','
    ;

Typeof_expression 的第一種形式是由 typeof 關鍵字後面加上括弧的 別所組成。The first form of typeof_expression consists of a typeof keyword followed by a parenthesized type. 此表單運算式的結果是所 System.Type 指定類型的物件。The result of an expression of this form is the System.Type object for the indicated type. System.Type任何指定的類型只有一個物件。There is only one System.Type object for any given type. 這表示針對型別  Ttypeof(T) == typeof(T) 一律為 true。This means that for a type T, typeof(T) == typeof(T) is always true. 類型 不可為 dynamicThe type cannot be dynamic.

第二種形式的 typeof_expression 是由 typeof 關鍵字後面加上括弧的 unbound_type_name 所組成。The second form of typeof_expression consists of a typeof keyword followed by a parenthesized unbound_type_name. Unbound_type_nameType_name (命名空間和類型) 名稱非常類似,不同之處在于 unbound_type_name 包含 generic_dimension_specifier 包含 type_name 的 type_argument_listAn unbound_type_name is very similar to a type_name (Namespace and type names) except that an unbound_type_name contains generic_dimension_specifier s where a type_name contains type_argument_list s. typeof_expression 的運算元是符合 unbound_type_nametype_name 之文法的標記序列時(亦即,當它不包含 generic_dimension_specifiertype_argument_list 時),會將標記序列視為 type_nameWhen the operand of a typeof_expression is a sequence of tokens that satisfies the grammars of both unbound_type_name and type_name, namely when it contains neither a generic_dimension_specifier nor a type_argument_list, the sequence of tokens is considered to be a type_name. Unbound_type_name 的意義會依照下列方式決定:The meaning of an unbound_type_name is determined as follows:

  • 藉由將每個 generic_dimension_specifier 取代為 object 每個 type_argument 都有相同數目的逗號和關鍵字,以將每個 type_argument_list 轉換成 type_name。Convert the sequence of tokens to a type_name by replacing each generic_dimension_specifier with a type_argument_list having the same number of commas and the keyword object as each type_argument.
  • 評估產生的 type_name,同時忽略所有類型參數條件約束。Evaluate the resulting type_name, while ignoring all type parameter constraints.
  • Unbound_type_name 會解析為未系結的泛型型別, (系結 和未系結的型別) )相關聯。The unbound_type_name resolves to the unbound generic type associated with the resulting constructed type (Bound and unbound types).

Typeof_expression 的結果是產生之未系結 System.Type 泛型型別的物件。The result of the typeof_expression is the System.Type object for the resulting unbound generic type.

第三種形式的 typeof_expression 是由 typeof 關鍵字後面加上括弧的關鍵字所組成 voidThe third form of typeof_expression consists of a typeof keyword followed by a parenthesized void keyword. 此表單的運算式結果是 System.Type 代表沒有類型的物件。The result of an expression of this form is the System.Type object that represents the absence of a type. 所傳回的類型物件與 typeof(void) 針對任何類型所傳回的類型物件不同。The type object returned by typeof(void) is distinct from the type object returned for any type. 這個特殊類型物件適用于允許在語言中反映方法的類別庫,而這些方法希望有方法來表示具有實例之任何方法的傳回類型,包括 void 方法 System.TypeThis special type object is useful in class libraries that allow reflection onto methods in the language, where those methods wish to have a way to represent the return type of any method, including void methods, with an instance of System.Type.

typeof運算子可用於類型參數。The typeof operator can be used on a type parameter. 結果是系結 System.Type 至型別參數之執行時間類型的物件。The result is the System.Type object for the run-time type that was bound to the type parameter. typeof運算子也可以用於已建立的型別或未系結的泛型型別, (系結和未系結的類型) 。The typeof operator can also be used on a constructed type or an unbound generic type (Bound and unbound types). 未系結 System.Type 泛型型別的物件與 System.Type 實例類型的物件不同。The System.Type object for an unbound generic type is not the same as the System.Type object of the instance type. 實例型別在執行時間一律是封閉的結構化型別,因此它的 System.Type 物件取決於使用中的執行時間型別引數,而未系結的泛型型別則沒有型別引數。The instance type is always a closed constructed type at run-time so its System.Type object depends on the run-time type arguments in use, while the unbound generic type has no type arguments.

範例The example

using System;

class X<T>
{
    public static void PrintTypes() {
        Type[] t = {
            typeof(int),
            typeof(System.Int32),
            typeof(string),
            typeof(double[]),
            typeof(void),
            typeof(T),
            typeof(X<T>),
            typeof(X<X<T>>),
            typeof(X<>)
        };
        for (int i = 0; i < t.Length; i++) {
            Console.WriteLine(t[i]);
        }
    }
}

class Test
{
    static void Main() {
        X<int>.PrintTypes();
    }
}

產生下列輸出:produces the following output:

System.Int32
System.Int32
System.String
System.Double[]
System.Void
System.Int32
X`1[System.Int32]
X`1[X`1[System.Int32]]
X`1[T]

請注意, intSystem.Int32 是相同的類型。Note that int and System.Int32 are the same type.

另請注意,的結果 typeof(X<>) 不會相依于型別引數,而是的結果 typeof(X<T>)Also note that the result of typeof(X<>) does not depend on the type argument but the result of typeof(X<T>) does.

checked 和 unchecked 運算子The checked and unchecked operators

checkedunchecked 運算子用來控制整數型別算數運算和轉換的 溢位檢查內容The checked and unchecked operators are used to control the overflow checking context for integral-type arithmetic operations and conversions.

checked_expression
    : 'checked' '(' expression ')'
    ;

unchecked_expression
    : 'unchecked' '(' expression ')'
    ;

checked運算子會評估已檢查內容中的包含運算式,而 unchecked 運算子會在未檢查的內容中評估包含的運算式。The checked operator evaluates the contained expression in a checked context, and the unchecked operator evaluates the contained expression in an unchecked context. Checked_expressionunchecked_expression 完全對應至 parenthesized_expression (括弧內的 運算式) ,但包含的運算式會在指定的溢位檢查內容中進行評估。A checked_expression or unchecked_expression corresponds exactly to a parenthesized_expression (Parenthesized expressions), except that the contained expression is evaluated in the given overflow checking context.

溢位檢查內容也可以透過 checked 和語句來控制, unchecked (checked 和 unchecked 語句) 。The overflow checking context can also be controlled through the checked and unchecked statements (The checked and unchecked statements).

下列作業會受到 checked 和運算子和語句所建立的溢位檢查內容影響 uncheckedThe following operations are affected by the overflow checking context established by the checked and unchecked operators and statements:

當上述其中一個作業產生的結果太大而無法在目的地類型中表示時,執行作業的內容會控制產生的行為:When one of the above operations produce a result that is too large to represent in the destination type, the context in which the operation is performed controls the resulting behavior:

  • checked 內容中,如果作業是常數運算式 (常數 運算式) ,就會發生編譯時期錯誤。In a checked context, if the operation is a constant expression (Constant expressions), a compile-time error occurs. 否則,當作業在執行時間執行時, System.OverflowException 就會擲回。Otherwise, when the operation is performed at run-time, a System.OverflowException is thrown.
  • unchecked 內容中,會捨棄不符合目的地類型的任何高序位位,藉以截斷結果。In an unchecked context, the result is truncated by discarding any high-order bits that do not fit in the destination type.

對於非常數運算式 (在執行時間) 評估且未以任何 checked 或運算子或語句括住的運算式 uncheckedunchecked 除非外部因素 (例如編譯器參數和執行環境設定) 呼叫進行評估,否則會使用預設溢位檢查內容 checkedFor non-constant expressions (expressions that are evaluated at run-time) that are not enclosed by any checked or unchecked operators or statements, the default overflow checking context is unchecked unless external factors (such as compiler switches and execution environment configuration) call for checked evaluation.

針對常數運算式 (可在編譯時期) 完整評估的運算式,預設溢位檢查內容一律為 checkedFor constant expressions (expressions that can be fully evaluated at compile-time), the default overflow checking context is always checked. 除非在內容中明確放置常數運算式,否則在 unchecked 運算式編譯時間評估期間發生的溢位一律會造成編譯時期錯誤。Unless a constant expression is explicitly placed in an unchecked context, overflows that occur during the compile-time evaluation of the expression always cause compile-time errors.

匿名函式的主體不會受到匿名函 checkedunchecked 發生的或內容影響。The body of an anonymous function is not affected by checked or unchecked contexts in which the anonymous function occurs.

在範例中In the example

class Test
{
    static readonly int x = 1000000;
    static readonly int y = 1000000;

    static int F() {
        return checked(x * y);      // Throws OverflowException
    }

    static int G() {
        return unchecked(x * y);    // Returns -727379968
    }

    static int H() {
        return x * y;               // Depends on default
    }
}

因為兩個運算式都無法在編譯時間進行評估,所以不會回報任何編譯時期錯誤。no compile-time errors are reported since neither of the expressions can be evaluated at compile-time. 在執行時間,方法會擲回 F System.OverflowException ,而方法會傳回 G -727379968 (超出範圍結果) 的較低32位。At run-time, the F method throws a System.OverflowException, and the G method returns -727379968 (the lower 32 bits of the out-of-range result). 方法的行為 H 取決於編譯的預設溢位檢查內容,但它與相同 F 或相同 GThe behavior of the H method depends on the default overflow checking context for the compilation, but it is either the same as F or the same as G.

在範例中In the example

class Test
{
    const int x = 1000000;
    const int y = 1000000;

    static int F() {
        return checked(x * y);      // Compile error, overflow
    }

    static int G() {
        return unchecked(x * y);    // Returns -727379968
    }

    static int H() {
        return x * y;               // Compile error, overflow
    }
}

在中評估常數運算式時所發生的溢 F H 位,會導致報告編譯時期錯誤,因為運算式是在內容中進行評估 checkedthe overflows that occur when evaluating the constant expressions in F and H cause compile-time errors to be reported because the expressions are evaluated in a checked context. 在中評估常數運算式時也會發生溢 G 位,但由於評估是在內容中發生 unchecked ,因此不會報告溢位。An overflow also occurs when evaluating the constant expression in G, but since the evaluation takes place in an unchecked context, the overflow is not reported.

checkedunchecked 運算子只會對包含在 " ( " 和 "" 標記內以程式集中的作業,影響溢位檢查內容 )The checked and unchecked operators only affect the overflow checking context for those operations that are textually contained within the "(" and ")" tokens. 運算子不會影響在評估包含的運算式時所叫用的函數成員。The operators have no effect on function members that are invoked as a result of evaluating the contained expression. 在範例中In the example

class Test
{
    static int Multiply(int x, int y) {
        return x * y;
    }

    static int F() {
        return checked(Multiply(1000000, 1000000));
    }
}

checked在中使用 F 並不會影響中的評估 x * y Multiply ,因此 x * y 會在預設溢位檢查內容中進行評估。the use of checked in F does not affect the evaluation of x * y in Multiply, so x * y is evaluated in the default overflow checking context.

unchecked在十六進位標記法中撰寫帶正負號整數類資料類型的常數時,運算子會很方便。The unchecked operator is convenient when writing constants of the signed integral types in hexadecimal notation. 例如:For example:

class Test
{
    public const int AllBits = unchecked((int)0xFFFFFFFF);

    public const int HighBit = unchecked((int)0x80000000);
}

上述的兩個十六進位常數都屬於型別 uintBoth of the hexadecimal constants above are of type uint. 因為常數是在 int 不含運算子的範圍之外,所以 unchecked 轉換成 int 會產生編譯時期錯誤。Because the constants are outside the int range, without the unchecked operator, the casts to int would produce compile-time errors.

checkedunchecked 運算子和語句可讓程式設計人員控制一些數值計算的特定層面。The checked and unchecked operators and statements allow programmers to control certain aspects of some numeric calculations. 不過,某些數值運算子的行為取決於其運算元的資料類型。However, the behavior of some numeric operators depends on their operands' data types. 例如,即使在明確的結構內,兩個小數位數一律會導致溢位例外狀況 uncheckedFor example, multiplying two decimals always results in an exception on overflow even within an explicitly unchecked construct. 同樣地,即使在明確的結構內,將兩個浮點數相乘也不會造成溢位例外狀況 checkedSimilarly, multiplying two floats never results in an exception on overflow even within an explicitly checked construct. 此外,其他運算子永遠不會受到檢查模式的影響,不論是預設或明確。In addition, other operators are never affected by the mode of checking, whether default or explicit.

預設值運算式Default value expressions

預設值運算式可用來取得預設值 (預設 值) 類型。A default value expression is used to obtain the default value (Default values) of a type. 通常會使用預設值運算式作為型別參數,因為如果型別參數是實值型別或參考型別,則可能不知道。Typically a default value expression is used for type parameters, since it may not be known if the type parameter is a value type or a reference type. (不會將常值轉換 null 為類型參數,除非已知類型參數是參考型別。 ) (No conversion exists from the null literal to a type parameter unless the type parameter is known to be a reference type.)

default_value_expression
    : 'default' '(' type ')'
    ;

如果 default_value_expression 中的 別在執行時間評估為參考型別,則結果會 null 轉換為該型別。If the type in a default_value_expression evaluates at run-time to a reference type, the result is null converted to that type. 如果 default_value_expression 中的 別在執行時間評估為實值型別,則結果會是 Value_type 的預設值 (預設的函數) 。If the type in a default_value_expression evaluates at run-time to a value type, the result is the value_type's default value (Default constructors).

如果型別是參考型別,或是已知為參考型別 (型別參數條件約束) 的型別參數,則 default_value_expression 是常數運算式 (常數運算式) 。A default_value_expression is a constant expression (Constant expressions) if the type is a reference type or a type parameter that is known to be a reference type (Type parameter constraints). 此外,如果類型是下列其中一個數值型別,則 default_value_expression 是常數運算式: sbyte 、、、、、、、、、、、、 byte short ushort int uint long ulong char float double decimal bool 或任何列舉型別。In addition, a default_value_expression is a constant expression if the type is one of the following value types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, or any enumeration type.

Nameof 運算式Nameof expressions

Nameof_expression 用來取得程式實體的名稱,做為常數位串。A nameof_expression is used to obtain the name of a program entity as a constant string.

nameof_expression
    : 'nameof' '(' named_entity ')'
    ;

named_entity
    : simple_name
    | named_entity_target '.' identifier type_argument_list?
    ;

named_entity_target
    : 'this'
    | 'base'
    | named_entity 
    | predefined_type 
    | qualified_alias_member
    ;

文法說, named_entity 運算元一律為運算式。Grammatically speaking, the named_entity operand is always an expression. 因為不是 nameof 保留的關鍵字,所以 nameof 運算式在語法上與簡單名稱的調用一律是不明確的 nameofBecause nameof is not a reserved keyword, a nameof expression is always syntactically ambiguous with an invocation of the simple name nameof. 基於相容性的理由,如果名稱查閱 (簡單名稱) 名稱 nameof 成功,則會將運算式視為 invocation_expression --不論調用是否合法。For compatibility reasons, if a name lookup (Simple names) of the name nameof succeeds, the expression is treated as an invocation_expression -- regardless of whether the invocation is legal. 否則就是 nameof_expressionOtherwise it is a nameof_expression.

Nameof_expressionnamed_entity 意義就是運算式的意義;也就是 simple_namebase_accessmember_accessThe meaning of the named_entity of a nameof_expression is the meaning of it as an expression; that is, either as a simple_name, a base_access or a member_access. 不過,在 簡單名稱成員存取 中描述的查閱會導致錯誤,因為在靜態內容中找到實例成員,所以 nameof_expression 不會產生這類錯誤。However, where the lookup described in Simple names and Member access results in an error because an instance member was found in a static context, a nameof_expression produces no such error.

這是一個編譯時期錯誤, named_entity 將方法群組指定為具有 type_argument_listIt is a compile-time error for a named_entity designating a method group to have a type_argument_list. 這是 named_entity_target 具有類型的編譯時期錯誤 dynamicIt is a compile time error for a named_entity_target to have the type dynamic.

Nameof_expression 是類型的常數運算式 string ,在執行時間不會有任何作用。A nameof_expression is a constant expression of type string, and has no effect at runtime. 具體來說,它的 named_entity 不會進行評估,而且會基於明確指派分析的用途而忽略 () 簡單運算式的一般規則Specifically, its named_entity is not evaluated, and is ignored for the purposes of definite assignment analysis (General rules for simple expressions). 它的值是選擇性最終 type_argument_list 之前 named_entity 的最後一個識別碼,轉換方式如下:Its value is the last identifier of the named_entity before the optional final type_argument_list, transformed in the following way:

  • 移除前置詞 " @ " (如果使用的話)。The prefix "@", if used, is removed.
  • 每個 unicode_escape_sequence 都會轉換成其對應的 unicode 字元。Each unicode_escape_sequence is transformed into its corresponding Unicode character.
  • 移除任何 formatting_charactersAny formatting_characters are removed.

這些是在測試識別碼是否相等時,在 識別碼 中套用的相同轉換。These are the same transformations applied in Identifiers when testing equality between identifiers.

TODO:範例TODO: examples

匿名方法運算式Anonymous method expressions

Anonymous_method_expression 是定義匿名函式的兩種方式之一。An anonymous_method_expression is one of two ways of defining an anonymous function. 這些會在匿名函式 運算式中進一步說明。These are further described in Anonymous function expressions.

一元運算子Unary operators

、、、、、、 ? + - ! ~ ++ -- 、Cast 和 await 運算子稱為一元運算子。The ?, +, -, !, ~, ++, --, cast, and await operators are called the unary operators.

unary_expression
    : primary_expression
    | null_conditional_expression
    | '+' unary_expression
    | '-' unary_expression
    | '!' unary_expression
    | '~' unary_expression
    | pre_increment_expression
    | pre_decrement_expression
    | cast_expression
    | await_expression
    | unary_expression_unsafe
    ;

如果 unary_expression 的運算元具有編譯時間型別 dynamic ,則會動態繫結 (動態繫結) 。If the operand of a unary_expression has the compile-time type dynamic, it is dynamically bound (Dynamic binding). 在此情況下, unary_expression 的編譯時間型別為 dynamic ,而且以下所述的解析將會在執行時間使用運算元的執行時間類型進行。In this case the compile-time type of the unary_expression is dynamic, and the resolution described below will take place at run-time using the run-time type of the operand.

Null 條件運算子Null-conditional operator

Null 條件運算子只會在運算元為非 null 時,才會將作業清單套用至其運算元。The null-conditional operator applies a list of operations to its operand only if that operand is non-null. 否則,套用運算子的結果為 nullOtherwise the result of applying the operator is null.

null_conditional_expression
    : primary_expression null_conditional_operations
    ;

null_conditional_operations
    : null_conditional_operations? '?' '.' identifier type_argument_list?
    | null_conditional_operations? '?' '[' argument_list ']'
    | null_conditional_operations '.' identifier type_argument_list?
    | null_conditional_operations '[' argument_list ']'
    | null_conditional_operations '(' argument_list? ')'
    ;

作業清單可以包含成員存取和專案存取作業 (這些作業本身可以是 null 條件式) 和調用。The list of operations can include member access and element access operations (which may themselves be null-conditional), as well as invocation.

例如,運算式 a.b?[0]?.c() 是具有 primary_expressionnull_conditional_expression a.bnull_conditional_operations ?[0] (null 條件式元素存取) , ?.c (null 條件式成員存取) 和 () (調用) 。For example, the expression a.b?[0]?.c() is a null_conditional_expression with a primary_expression a.b and null_conditional_operations ?[0] (null-conditional element access), ?.c (null-conditional member access) and () (invocation).

對於 E 具有 primary_expression 的 null_conditional_expression P ,讓我們使用以 E0 程式方式取得的運算式, ? 從每一個有一個的 null_conditional_operations 中移除前置的運算式 EFor a null_conditional_expression E with a primary_expression P, let E0 be the expression obtained by textually removing the leading ? from each of the null_conditional_operations of E that have one. 在概念上, E0 如果不是由所表示的任何 null 檢查都找不到,就會評估運算式 ? nullConceptually, E0 is the expression that will be evaluated if none of the null checks represented by the ?s do find a null.

此外,讓我們 E1 以程式方式取得運算式, ? 從的第一個 null_conditional_operations 中移除前置 EAlso, let E1 be the expression obtained by textually removing the leading ? from just the first of the null_conditional_operations in E. 如果只有一個) 或另一個 null_conditional_expression,這可能會導致 主要運算式 (?This may lead to a primary-expression (if there was just one ?) or to another null_conditional_expression.

例如,如果 E 是運算式,則 a.b?[0]?.c() 為運算式, E0 a.b[0].c()E1 是運算式 a.b[0]?.c()For example, if E is the expression a.b?[0]?.c(), then E0 is the expression a.b[0].c() and E1 is the expression a.b[0]?.c().

如果 E0 分類為 nothing,則 E 會分類為 nothing。If E0 is classified as nothing, then E is classified as nothing. 否則會將 E 分類為值。Otherwise E is classified as a value.

E0E1 可用來判斷的意義 EE0 and E1 are used to determine the meaning of E:

  • 如果 E 發生做為 statement_expression 的意義 E 就與語句相同If E occurs as a statement_expression the meaning of E is the same as the statement

    if ((object)P != null) E1;
    

    但只會評估 P 一次。except that P is evaluated only once.

  • 否則,如果 E0 分類為任何未發生的編譯時期錯誤,則為。Otherwise, if E0 is classified as nothing a compile-time error occurs.

  • 否則,請讓 T0 成為的型別 E0Otherwise, let T0 be the type of E0.

    • 如果 T0 是不知道為參考型別或不可為 null 實值型別的型別參數,則會發生編譯時期錯誤。If T0 is a type parameter that is not known to be a reference type or a non-nullable value type, a compile-time error occurs.

    • 如果 T0 是不可為 null 的實值型別,則的型別 ET0? ,且的意義 EIf T0 is a non-nullable value type, then the type of E is T0?, and the meaning of E is the same as

      ((object)P == null) ? (T0?)null : E1
      

      P 只會評估一次。except that P is evaluated only once.

    • 否則,E 的類型為 T0,而 E 的意義與Otherwise the type of E is T0, and the meaning of E is the same as

      ((object)P == null) ? null : E1
      

      P 只會評估一次。except that P is evaluated only once.

如果 E1 本身為 null_conditional_expression,則會再次套用這些規則,然後將測試嵌套, null 直到沒有進一步 ? 的,且運算式已減少到主要運算式為止 E0If E1 is itself a null_conditional_expression, then these rules are applied again, nesting the tests for null until there are no further ?'s, and the expression has been reduced all the way down to the primary-expression E0.

例如,如果運算式以 a.b?[0]?.c() 語句運算式的形式出現,就像在語句中一樣:For example, if the expression a.b?[0]?.c() occurs as a statement-expression, as in the statement:

a.b?[0]?.c();

其意義相當於:its meaning is equivalent to:

if (a.b != null) a.b[0]?.c();

這同樣等同于:which again is equivalent to:

if (a.b != null) if (a.b[0] != null) a.b[0].c();

唯一的 a.b 例外 a.b[0] 是,而且只會評估一次。Except that a.b and a.b[0] are evaluated only once.

如果發生在使用其值的內容中,如下所示:If it occurs in a context where its value is used, as in:

var x = a.b?[0]?.c();

而且假設最後一個調用的型別不是不可為 null 的實值型別,其意義就相當於:and assuming that the type of the final invocation is not a non-nullable value type, its meaning is equivalent to:

var x = (a.b == null) ? null : (a.b[0] == null) ? null : a.b[0].c();

唯一的 a.b 例外 a.b[0] 是,而且只會評估一次。except that a.b and a.b[0] are evaluated only once.

Null 條件運算式作為投影初始化運算式Null-conditional expressions as projection initializers

Null 條件運算式只允許做為 anonymous_object_creation_expression (匿名物件建立運算式member_declarator ,) 如果它的結尾是 (選擇性的 null 條件式) 成員存取。A null-conditional expression is only allowed as a member_declarator in an anonymous_object_creation_expression (Anonymous object creation expressions) if it ends with an (optionally null-conditional) member access. 文法,此需求可表示為:Grammatically, this requirement can be expressed as:

null_conditional_member_access
    : primary_expression null_conditional_operations? '?' '.' identifier type_argument_list?
    | primary_expression null_conditional_operations '.' identifier type_argument_list?
    ;

這是上述 null_conditional_expression 文法的特殊案例。This is a special case of the grammar for null_conditional_expression above. Member_declarator匿名物件建立運算式中的生產環境只包含 null_conditional_member_accessThe production for member_declarator in Anonymous object creation expressions then includes only null_conditional_member_access.

Null 條件運算式作為語句運算式Null-conditional expressions as statement expressions

Null 條件運算式只允許做為 statement_expression 的 (運算式語句) 如果其結尾是調用。A null-conditional expression is only allowed as a statement_expression (Expression statements) if it ends with an invocation. 文法,此需求可表示為:Grammatically, this requirement can be expressed as:

null_conditional_invocation_expression
    : primary_expression null_conditional_operations '(' argument_list? ')'
    ;

這是上述 null_conditional_expression 文法的特殊案例。This is a special case of the grammar for null_conditional_expression above. 運算式語句statement_expression 的生產環境只包含 null_conditional_invocation_expressionThe production for statement_expression in Expression statements then includes only null_conditional_invocation_expression.

一元加號運算子Unary plus operator

若為表單的作業 +x ,則會套用一元運算子多載解析 (一元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form +x, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. 預先定義的一元加法運算子如下:The predefined unary plus operators are:

int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);

針對上述每個運算子,結果只是運算元的值。For each of these operators, the result is simply the value of the operand.

一元減號運算子Unary minus operator

若為表單的作業 -x ,則會套用一元運算子多載解析 (一元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form -x, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. 預先定義的負運算子如下:The predefined negation operators are:

  • 整數否定:Integer negation:

    int operator -(int x);
    long operator -(long x);
    

    結果的計算方式是 x 從零減去。The result is computed by subtracting x from zero. 如果的值 x 是運算元類型的最小可表示值 (-2 ^ 31 代表 int 或-2 ^ 63 表示 long) ,則在 x 運算元類型內無法表示的數學否定。If the value of x is the smallest representable value of the operand type (-2^31 for int or -2^63 for long), then the mathematical negation of x is not representable within the operand type. 如果發生這種情況 checked ,就會擲回, System.OverflowException 如果它發生在 unchecked 內容中,結果就是運算元的值,而且不會報告溢位。If this occurs within a checked context, a System.OverflowException is thrown; if it occurs within an unchecked context, the result is the value of the operand and the overflow is not reported.

    如果負運算子的運算元是型別,則 uint 會轉換成類型 long ,而結果的型別為 longIf the operand of the negation operator is of type uint, it is converted to type long, and the type of the result is long. 例外狀況是允許將 int 值-2147483648 (-2 ^ 31) 撰寫為) 整數常 值的十進位 (整數常值的規則。An exception is the rule that permits the int value -2147483648 (-2^31) to be written as a decimal integer literal (Integer literals).

    如果負運算子的運算元是型別 ulong ,就會發生編譯階段錯誤。If the operand of the negation operator is of type ulong, a compile-time error occurs. 例外狀況是允許將 long 值-9223372036854775808 (-2 ^ 63) 撰寫為) 整數常 值的十進位整數 (常值的規則。An exception is the rule that permits the long value -9223372036854775808 (-2^63) to be written as a decimal integer literal (Integer literals).

  • 浮點數否定:Floating-point negation:

    float operator -(float x);
    double operator -(double x);
    

    結果會是 x 其正負號反轉的值。The result is the value of x with its sign inverted. 如果 x 是 nan,則結果也是 nan。If x is NaN, the result is also NaN.

  • 小數否定:Decimal negation:

    decimal operator -(decimal x);
    

    結果的計算方式是 x 從零減去。The result is computed by subtracting x from zero. Decimal 負號相當於使用類型的一元減號運算子 System.DecimalDecimal negation is equivalent to using the unary minus operator of type System.Decimal.

邏輯否定運算子Logical negation operator

若為表單的作業 !x ,則會套用一元運算子多載解析 (一元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form !x, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. 只有一個預先定義的邏輯負運算子存在:Only one predefined logical negation operator exists:

bool operator !(bool x);

這個運算子會計算運算元的邏輯否定:如果運算元為 true ,則結果為 falseThis operator computes the logical negation of the operand: If the operand is true, the result is false. 如果運算元為 false,則結果為 trueIf the operand is false, the result is true.

位元補充運算子 Bitwise complement operator

若為表單的作業 ~x ,則會套用一元運算子多載解析 (一元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form ~x, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. 預先定義的位補數運算子為:The predefined bitwise complement operators are:

int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);

針對上述每個運算子,作業的結果為的位補數 xFor each of these operators, the result of the operation is the bitwise complement of x.

每個列舉類型都會 E 隱含地提供下列位補數運算子:Every enumeration type E implicitly provides the following bitwise complement operator:

E operator ~(E x);

評估的結果 ~x (其中 x 是具有基礎類型的列舉型別運算式 E )與 U 評估完全相同, (E)(~(U)x) 不同之處在于的轉換 E 一律會如同在 unchecked (checked 和 unchecked 運算子) 的內容中一樣地執行。The result of evaluating ~x, where x is an expression of an enumeration type E with an underlying type U, is exactly the same as evaluating (E)(~(U)x), except that the conversion to E is always performed as if in an unchecked context (The checked and unchecked operators).

前置遞增和遞減運算子Prefix increment and decrement operators

pre_increment_expression
    : '++' unary_expression
    ;

pre_decrement_expression
    : '--' unary_expression
    ;

前置遞增或遞減運算的運算元必須是分類為變數、屬性存取或索引子存取的運算式。The operand of a prefix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. 運算的結果是與運算元相同類型的值。The result of the operation is a value of the same type as the operand.

如果前置遞增或遞減運算的運算元是屬性或索引子存取,則屬性或索引子必須同時具有 getset 存取子。If the operand of a prefix increment or decrement operation is a property or indexer access, the property or indexer must have both a get and a set accessor. 如果不是這種情況,就會發生系結階段錯誤。If this is not the case, a binding-time error occurs.

一元運算子多載解析 (一元運算子 多載解析) 適用于選取特定的運算子執行。Unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 預先 ++ 定義 -- 的和運算子存在於下列類型:、、、、、、、、、、、 sbyte byte short ushort int uint long ulong char float double decimal 和任何列舉類型。Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. 預先定義的運算子會傳回將 ++ 1 加入運算元所產生的值,而預先定義的運算子會傳回 -- 從運算元減去1所產生的值。The predefined ++ operators return the value produced by adding 1 to the operand, and the predefined -- operators return the value produced by subtracting 1 from the operand. checked 內容中,如果這個加法或減法的結果超出結果型別的範圍,且結果型別是整數型別或列舉型別, System.OverflowException 就會擲回。In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a System.OverflowException is thrown.

表單的前置遞增或遞減作業的執行時間處理, ++x 或是 --x 由下列步驟所組成:The run-time processing of a prefix increment or decrement operation of the form ++x or --x consists of the following steps:

  • 如果 x 分類為變數:If x is classified as a variable:
    • x 會評估以產生變數。x is evaluated to produce the variable.
    • 使用的值 x 做為其引數來叫用選取的運算子。The selected operator is invoked with the value of x as its argument.
    • 運算子所傳回的值會儲存在評估所指定的位置 xThe value returned by the operator is stored in the location given by the evaluation of x.
    • 運算子傳回的值會成為作業的結果。The value returned by the operator becomes the result of the operation.
  • 如果 x 分類為屬性或索引子存取:If x is classified as a property or indexer access:
    • 如果不是) ,實例運算式 (x static ,且如果 x 是與相關聯的索引子存取) ,則會評估引數 (清單 x ,且結果會用於後續 getset 存取子調用。The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent get and set accessor invocations.
    • 叫用的 get 存取子 xThe get accessor of x is invoked.
    • 使用存取子所傳回的值 get 做為其引數,以叫用選取的運算子。The selected operator is invoked with the value returned by the get accessor as its argument.
    • set 存取子 x 會以運算子傳回的值做為其引數來叫用 valueThe set accessor of x is invoked with the value returned by the operator as its value argument.
    • 運算子傳回的值會成為作業的結果。The value returned by the operator becomes the result of the operation.

++-- 運算子也支援後置標記法 (後置遞增和遞減運算子) 。The ++ and -- operators also support postfix notation (Postfix increment and decrement operators). 或的結果通常是在作業 x++ x-- 之前的值 x ,而或的結果 ++x 是作業之後的 --xxTypically, the result of x++ or x-- is the value of x before the operation, whereas the result of ++x or --x is the value of x after the operation. 在任一種情況下, x 其本身在作業之後都具有相同的值。In either case, x itself has the same value after the operation.

operator++ operator-- 可以使用後置或前置標記法來叫用或實作為。An operator++ or operator-- implementation can be invoked using either postfix or prefix notation. 這兩種標記法不能有個別的運算子實現。It is not possible to have separate operator implementations for the two notations.

轉換運算式Cast expressions

Cast_expression 用來明確地將運算式轉換成指定的型別。A cast_expression is used to explicitly convert an expression to a given type.

cast_expression
    : '(' type ')' unary_expression
    ;

表單的 cast_expression (T)E ,其中 T類型 ,而 Eunary_expression,會執行明確的轉換, (將值的 明確轉換) E 為類型 TA cast_expression of the form (T)E, where T is a type and E is a unary_expression, performs an explicit conversion (Explicit conversions) of the value of E to type T. 如果沒有明確的轉換 E ,則 T 會發生系結階段錯誤。If no explicit conversion exists from E to T, a binding-time error occurs. 否則,結果會是明確轉換所產生的值。Otherwise, the result is the value produced by the explicit conversion. 結果一律會分類為值,即使代表變數也是一樣 EThe result is always classified as a value, even if E denotes a variable.

Cast_expression 的文法會導致特定的語法歧義。The grammar for a cast_expression leads to certain syntactic ambiguities. 例如,您 (x)-y 可以將運算式解釋為 cast_expression (將轉換為 -y 類型 x) 或 additive_expression 與計算值的 parenthesized_expression (結合 x - y)For example, the expression (x)-y could either be interpreted as a cast_expression (a cast of -y to type x) or as an additive_expression combined with a parenthesized_expression (which computes the value x - y).

若要解決 cast_expression 不明確的問題,請注意下列規則:只有在下列其中一個條件成立時,才會將一或多個 標記 () 空白字元 的序列,視為 cast_expression 的開頭:To resolve cast_expression ambiguities, the following rule exists: A sequence of one or more token s (White space) enclosed in parentheses is considered the start of a cast_expression only if at least one of the following are true:

  • 標記的序列是 類型 的正確文法,但不是 運算式 的正確文法。The sequence of tokens is correct grammar for a type, but not for an expression.
  • Token 的順序是正確的 別文法,而緊接在右括弧後面的 token 是 token " ~ "、token " ! "、token " ( "、 (Unicode 字元 escape 序列) 的 識別碼 值 (值) 或任何 關鍵字 (關鍵字) 除了 as 和之外 isThe sequence of tokens is correct grammar for a type, and the token immediately following the closing parentheses is the token "~", the token "!", the token "(", an identifier (Unicode character escape sequences), a literal (Literals), or any keyword (Keywords) except as and is.

上述「正確的文法」一詞表示權杖的順序必須符合特定的文法生產。The term "correct grammar" above means only that the sequence of tokens must conform to the particular grammatical production. 明確地說,它並不會考慮任何組成識別碼的實際意義。It specifically does not consider the actual meaning of any constituent identifiers. 例如,如果 xy 是識別碼,則 x.y 為類型的正確文法,即使 x.y 實際上未表示型別也是一樣。For example, if x and y are identifiers, then x.y is correct grammar for a type, even if x.y doesn't actually denote a type.

從去除混淆規則開始,如果 xy 是識別碼、 (x)y 、和都 (x)(y) (x)(-y)cast_expression s,但卻 (x)-y 不是,即使 x 識別型別也是一樣。From the disambiguation rule it follows that, if x and y are identifiers, (x)y, (x)(y), and (x)(-y) are cast_expression s, but (x)-y is not, even if x identifies a type. 但是,如果 x 是識別預先定義之類型 (例如) 的關鍵字 int ,則這四種形式都會 cast_expression s (,因為這類關鍵字) 本身可能不是運算式。However, if x is a keyword that identifies a predefined type (such as int), then all four forms are cast_expression s (because such a keyword could not possibly be an expression by itself).

Await 運算式Await expressions

Await 運算子用來暫止封閉非同步函式的評估,直到運算元所表示的非同步作業完成為止。The await operator is used to suspend evaluation of the enclosing async function until the asynchronous operation represented by the operand has completed.

await_expression
    : 'await' unary_expression
    ;

Await_expression 只能在非同步函式的主體中使用 (非同步函式) 。An await_expression is only allowed in the body of an async function (Async functions). 在最接近的封入非同步函式內, await_expression 可能不會出現在這些位置:Within the nearest enclosing async function, an await_expression may not occur in these places:

  • 在嵌套 (非非同步) 匿名函式中Inside a nested (non-async) anonymous function
  • lock_statement 的區塊內Inside the block of a lock_statement
  • 在 unsafe 內容中In an unsafe context

請注意, await_expression 不能出現在 query_expression 的大部分位置中,因為它們在語法上是轉換成使用非非同步 lambda 運算式。Note that an await_expression cannot occur in most places within a query_expression, because those are syntactically transformed to use non-async lambda expressions.

在非同步函式內, await 不能當做識別碼使用。Inside of an async function, await cannot be used as an identifier. 因此,在 await 運算式和涉及識別碼的各種運算式之間,不會有語法上的混淆。There is therefore no syntactic ambiguity between await-expressions and various expressions involving identifiers. 在非同步函式之外,會 await 作為一般識別碼。Outside of async functions, await acts as a normal identifier.

Await_expression 的運算元稱為 *task _。The operand of an await_expression is called the *task _. 它代表在評估 _await_expression * 時,可能或可能不會完成的非同步作業。It represents an asynchronous operation that may or may not be complete at the time the _await_expression* is evaluated. Await 運算子的目的是暫停執行封閉的非同步函式,直到等候的工作完成,然後取得其結果。The purpose of the await operator is to suspend execution of the enclosing async function until the awaited task is complete, and then obtain its outcome.

可等候運算式Awaitable expressions

需要 可等候 await 運算式的工作。The task of an await expression is required to be awaitable. t如果下列其中一項成立,就會可等候運算式:An expression t is awaitable if one of the following holds:

  • t 是編譯時間類型 dynamict is of compile time type dynamic
  • t 具有可存取的實例或 GetAwaiter 使用不含參數的擴充方法,而且沒有任何型別參數,以及下列所有的傳回型 A 別:t has an accessible instance or extension method called GetAwaiter with no parameters and no type parameters, and a return type A for which all of the following hold:
    • A``System.Runtime.CompilerServices.INotifyCompletion為了簡潔起見,會實 (的介面 INotifyCompletion) A implements the interface System.Runtime.CompilerServices.INotifyCompletion (hereafter known as INotifyCompletion for brevity)
    • A具有類型的可存取、可讀取的實例屬性 IsCompleted``boolA has an accessible, readable instance property IsCompleted of type bool
    • A 具有 GetResult 無參數且沒有類型參數的可存取的實例方法A has an accessible instance method GetResult with no parameters and no type parameters

方法的用途 GetAwaiter 是取得工作的 *awaiter _。The purpose of the GetAwaiter method is to obtain an *awaiter _ for the task. 此類型 A 稱為 await 運算式的 _ *awaiter 類型**。The type A is called the _ awaiter type* for the await expression.

屬性的目的 IsCompleted 是要判斷工作是否已完成。The purpose of the IsCompleted property is to determine if the task is already complete. 若是如此,就不需要暫停評估。If so, there is no need to suspend evaluation.

方法的用途是將「 INotifyCompletion.OnCompleted 接續」註冊至工作,也就是 System.Action 在工作完成之後,將會叫用) 類型的委派 (。The purpose of the INotifyCompletion.OnCompleted method is to sign up a "continuation" to the task; i.e. a delegate (of type System.Action) that will be invoked once the task is complete.

方法的用途 GetResult 是在工作完成之後取得工作的結果。The purpose of the GetResult method is to obtain the outcome of the task once it is complete. 這項結果可能會成功完成,可能是結果值,也可能是方法擲回的例外狀況 GetResultThis outcome may be successful completion, possibly with a result value, or it may be an exception which is thrown by the GetResult method.

Await 運算式的分類Classification of await expressions

運算式的 await t 分類方式與運算式相同 (t).GetAwaiter().GetResult()The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult(). 因此,如果的傳回型 GetResult 別為 void ,則 await_expression 分類為 nothing。Thus, if the return type of GetResult is void, the await_expression is classified as nothing. 如果它有非 void 的傳回型別 T ,則會將 await_expression 分類為類型的值 TIf it has a non-void return type T, the await_expression is classified as a value of type T.

Await 運算式的執行時間評估Runtime evaluation of await expressions

在執行時間,運算式的 await t 評估方式如下:At runtime, the expression await t is evaluated as follows:

  • a 由評估運算式來取得 awaiter (t).GetAwaiter()An awaiter a is obtained by evaluating the expression (t).GetAwaiter().
  • bool b 由評估運算式來取得 (a).IsCompletedA bool b is obtained by evaluating the expression (a).IsCompleted.
  • 如果 b 為, false 則評估取決於是否 a 執行介面 (接下來 System.Runtime.CompilerServices.ICriticalNotifyCompletion 稱為 ICriticalNotifyCompletion 以求簡潔) 。If b is false then evaluation depends on whether a implements the interface System.Runtime.CompilerServices.ICriticalNotifyCompletion (hereafter known as ICriticalNotifyCompletion for brevity). 這項檢查是在系結階段完成;也就是在執行時間 a 中,如果有編譯時間類型,則為 dynamic ,否則為。This check is done at binding time; i.e. at runtime if a has the compile time type dynamic, and at compile time otherwise. r) 的 (非同步 函式表示繼續委派:Let r denote the resumption delegate (Async functions):
    • 如果 a 未執行 ICriticalNotifyCompletion ,則 (a as (INotifyCompletion)).OnCompleted(r) 會評估運算式。If a does not implement ICriticalNotifyCompletion, then the expression (a as (INotifyCompletion)).OnCompleted(r) is evaluated.
    • 如果 a 確實執行 ICriticalNotifyCompletion ,則 (a as (ICriticalNotifyCompletion)).UnsafeOnCompleted(r) 會評估運算式。If a does implement ICriticalNotifyCompletion, then the expression (a as (ICriticalNotifyCompletion)).UnsafeOnCompleted(r) is evaluated.
    • 然後評估會暫停,控制權會傳回給 async 函式的目前呼叫者。Evaluation is then suspended, and control is returned to the current caller of the async function.
  • 在 (if) 之後 b ,或在稍後叫用繼續 true 委派 (如果 b false) ,則 (a).GetResult() 會評估運算式。Either immediately after (if b was true), or upon later invocation of the resumption delegate (if b was false), the expression (a).GetResult() is evaluated. 如果它傳回值,該值就是 await_expression 的結果。If it returns a value, that value is the result of the await_expression. 否則結果為 nothing。Otherwise the result is nothing.

介面方法的 awaiter 執行 INotifyCompletion.OnCompleted ,而且 ICriticalNotifyCompletion.UnsafeOnCompleted 應該會造成 r 最多叫用委派一次。An awaiter's implementation of the interface methods INotifyCompletion.OnCompleted and ICriticalNotifyCompletion.UnsafeOnCompleted should cause the delegate r to be invoked at most once. 否則,封閉式非同步函式的行為是未定義的。Otherwise, the behavior of the enclosing async function is undefined.

算術運算子Arithmetic operators

*、、 / %+- 運算子稱為算術運算子。The *, /, %, +, and - operators are called the arithmetic operators.

multiplicative_expression
    : unary_expression
    | multiplicative_expression '*' unary_expression
    | multiplicative_expression '/' unary_expression
    | multiplicative_expression '%' unary_expression
    ;

additive_expression
    : multiplicative_expression
    | additive_expression '+' multiplicative_expression
    | additive_expression '-' multiplicative_expression
    ;

如果算術運算子的運算元具有編譯時間型別 dynamic ,則運算式會動態繫結 (動態繫結) 。If an operand of an arithmetic operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). 在此情況下,運算式的編譯時間型別是 dynamic ,而且下面所述的解析將會在執行時間使用具有編譯時間類型之運算元的執行時間型別進行 dynamicIn this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

乘法運算子Multiplication operator

針對格式的作業 x * y ,二元運算子多載解析 (二元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form x * y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

預先定義的乘法運算子如下所示。The predefined multiplication operators are listed below. 運算子會計算和的產品 x yThe operators all compute the product of x and y.

  • 整數乘法:Integer multiplication:

    int operator *(int x, int y);
    uint operator *(uint x, uint y);
    long operator *(long x, long y);
    ulong operator *(ulong x, ulong y);
    

    checked 內容中,如果產品超出結果型別的範圍, System.OverflowException 就會擲回。In a checked context, if the product is outside the range of the result type, a System.OverflowException is thrown. unchecked 內容中,不會報告溢位,而且會捨棄結果型別範圍之外的任何大量高序位位。In an unchecked context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded.

  • 浮點數乘法:Floating-point multiplication:

    float operator *(float x, float y);
    double operator *(double x, double y);
    

    會根據 IEEE 754 算術的規則來計算產品。The product is computed according to the rules of IEEE 754 arithmetic. 下表列出非零有限值、零、無限大和 NaN 所有可能組合的結果。The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. 在下表中,xy 是正有限值。In the table, x and y are positive finite values. zx * y 的結果。z is the result of x * y. 如果結果對目的地類型而言太大,則 z 為無限大。If the result is too large for the destination type, z is infinity. 如果結果對目的地類型而言太小,則 z 為零。If the result is too small for the destination type, z is zero.

    +y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    +x+x +z+z -Z-z +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    -X-x -Z-z +z+z -0-0 +0+0 -inf-inf +inf+inf NaNNaN
    +0+0 +0+0 -0-0 +0+0 -0-0 NaNNaN NaNNaN NaNNaN
    -0-0 -0-0 +0+0 -0-0 +0+0 NaNNaN NaNNaN NaNNaN
    +inf+inf +inf+inf -inf-inf NaNNaN NaNNaN +inf+inf -inf-inf NaNNaN
    -inf-inf -inf-inf +inf+inf NaNNaN NaNNaN -inf-inf +inf+inf NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • 十進位乘法:Decimal multiplication:

    decimal operator *(decimal x, decimal y);
    

    如果產生的值太大而無法以格式表示 decimalSystem.OverflowException 就會擲回。If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. 如果結果值太小而無法以 decimal 格式表示,則結果為零。If the result value is too small to represent in the decimal format, the result is zero. 結果的小數位數是在任何四捨五入之前,這是兩個運算元之刻度的總和。The scale of the result, before any rounding, is the sum of the scales of the two operands.

    Decimal 乘法相當於使用類型的乘法運算子 System.DecimalDecimal multiplication is equivalent to using the multiplication operator of type System.Decimal.

除法運算子Division operator

針對格式的作業 x / y ,二元運算子多載解析 (二元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form x / y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

下列是預先定義的除法運算子。The predefined division operators are listed below. 運算子會計算和的商 x yThe operators all compute the quotient of x and y.

  • 整數除法:Integer division:

    int operator /(int x, int y);
    uint operator /(uint x, uint y);
    long operator /(long x, long y);
    ulong operator /(ulong x, ulong y);
    

    如果右運算元的值為零, System.DivideByZeroException 則會擲回。If the value of the right operand is zero, a System.DivideByZeroException is thrown.

    相除會將結果四捨五入至零。The division rounds the result towards zero. 因此,結果的絕對值會是小於或等於兩個運算元之商絕對值的最大可能整數。Thus the absolute value of the result is the largest possible integer that is less than or equal to the absolute value of the quotient of the two operands. 當兩個運算元的正負號相同時,如果兩個運算元具有相同的正負號,則結果為零或正數。The result is zero or positive when the two operands have the same sign and zero or negative when the two operands have opposite signs.

    如果左運算元是最小的可 int long 表示值或值,右邊的運算元為 -1 ,就會發生溢位。If the left operand is the smallest representable int or long value and the right operand is -1, an overflow occurs. checked 內容中,這會造成 System.ArithmeticException) 擲回 (或子類別。In a checked context, this causes a System.ArithmeticException (or a subclass thereof) to be thrown. unchecked 內容中,它會在執行時定義為是否擲 System.ArithmeticException 回 (或子類別) ,或溢位未報告,且結果值為左運算元的值。In an unchecked context, it is implementation-defined as to whether a System.ArithmeticException (or a subclass thereof) is thrown or the overflow goes unreported with the resulting value being that of the left operand.

  • 浮點數除法:Floating-point division:

    float operator /(float x, float y);
    double operator /(double x, double y);
    

    商會根據 IEEE 754 算術的規則來計算。The quotient is computed according to the rules of IEEE 754 arithmetic. 下表列出非零有限值、零、無限大和 NaN 所有可能組合的結果。The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. 在下表中,xy 是正有限值。In the table, x and y are positive finite values. zx / y 的結果。z is the result of x / y. 如果結果對目的地類型而言太大,則 z 為無限大。If the result is too large for the destination type, z is infinity. 如果結果對目的地類型而言太小,則 z 為零。If the result is too small for the destination type, z is zero.

    +y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    +x+x +z+z -Z-z +inf+inf -inf-inf +0+0 -0-0 NaNNaN
    -X-x -Z-z +z+z -inf-inf +inf+inf -0-0 +0+0 NaNNaN
    +0+0 +0+0 -0-0 NaNNaN NaNNaN +0+0 -0-0 NaNNaN
    -0-0 -0-0 +0+0 NaNNaN NaNNaN -0-0 +0+0 NaNNaN
    +inf+inf +inf+inf -inf-inf +inf+inf -inf-inf NaNNaN NaNNaN NaNNaN
    -inf-inf -inf-inf +inf+inf -inf-inf +inf+inf NaNNaN NaNNaN NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • 小數除法:Decimal division:

    decimal operator /(decimal x, decimal y);
    

    如果右運算元的值為零, System.DivideByZeroException 則會擲回。If the value of the right operand is zero, a System.DivideByZeroException is thrown. 如果產生的值太大而無法以格式表示 decimalSystem.OverflowException 就會擲回。If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. 如果結果值太小而無法以 decimal 格式表示,則結果為零。If the result value is too small to represent in the decimal format, the result is zero. 結果的小數位數是將結果等於最接近的可表示十進位值的最小小數位數。The scale of the result is the smallest scale that will preserve a result equal to the nearest representable decimal value to the true mathematical result.

    小數除法相當於使用類型的除法運算子 System.DecimalDecimal division is equivalent to using the division operator of type System.Decimal.

餘數運算子Remainder operator

針對格式的作業 x % y ,二元運算子多載解析 (二元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form x % y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

預先定義的餘數運算子如下所示。The predefined remainder operators are listed below. 運算子會計算和之間相除的餘數 x yThe operators all compute the remainder of the division between x and y.

  • 整數餘數:Integer remainder:

    int operator %(int x, int y);
    uint operator %(uint x, uint y);
    long operator %(long x, long y);
    ulong operator %(ulong x, ulong y);
    

    的結果 x % y 是所產生的值 x - (x / y) * yThe result of x % y is the value produced by x - (x / y) * y. 如果 y 為零, System.DivideByZeroException 則會擲回。If y is zero, a System.DivideByZeroException is thrown.

    如果左運算元是最小 int 值或 long 值,右邊運算元是 -1System.OverflowException 則會擲回。If the left operand is the smallest int or long value and the right operand is -1, a System.OverflowException is thrown. 在沒有任何情況下 x % y ,會擲回例外狀況, x / y 而不會擲回例外狀況。In no case does x % y throw an exception where x / y would not throw an exception.

  • 浮點數餘數:Floating-point remainder:

    float operator %(float x, float y);
    double operator %(double x, double y);
    

    下表列出非零有限值、零、無限大和 NaN 所有可能組合的結果。The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. 在下表中,xy 是正有限值。In the table, x and y are positive finite values. zx % y 和計算的結果 x - n * y ,其中 n 是小於或等於的最大可能整數 x / yz is the result of x % y and is computed as x - n * y, where n is the largest possible integer that is less than or equal to x / y. 這種計算餘數的方法與用於整數運算元的方法類似,但不同于 IEEE 754 定義 (, n 也就是最接近) 的整數 x / yThis method of computing the remainder is analogous to that used for integer operands, but differs from the IEEE 754 definition (in which n is the integer closest to x / y).

    +y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    +x+x +z+z +z+z NaNNaN NaNNaN xx xx NaNNaN
    -X-x -Z-z -Z-z NaNNaN NaNNaN -X-x -X-x NaNNaN
    +0+0 +0+0 +0+0 NaNNaN NaNNaN +0+0 +0+0 NaNNaN
    -0-0 -0-0 -0-0 NaNNaN NaNNaN -0-0 -0-0 NaNNaN
    +inf+inf NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
    -inf-inf NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • 小數餘數:Decimal remainder:

    decimal operator %(decimal x, decimal y);
    

    如果右運算元的值為零, System.DivideByZeroException 則會擲回。If the value of the right operand is zero, a System.DivideByZeroException is thrown. 在任何四捨五入之前,結果的小數位數是兩個運算元的最大刻度,而結果的正負號(如果非零)則與相同 xThe scale of the result, before any rounding, is the larger of the scales of the two operands, and the sign of the result, if non-zero, is the same as that of x.

    小數餘數相當於使用類型的餘數運算子 System.DecimalDecimal remainder is equivalent to using the remainder operator of type System.Decimal.

加法運算子Addition operator

針對格式的作業 x + y ,二元運算子多載解析 (二元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form x + y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

預先定義的加法運算子如下所示。The predefined addition operators are listed below. 針對數值和列舉類型,預先定義的加法運算子會計算兩個運算元的總和。For numeric and enumeration types, the predefined addition operators compute the sum of the two operands. 當一個或兩個運算元的類型為 string 時,預先定義的加法運算子會串連運算元的字串表示。When one or both operands are of type string, the predefined addition operators concatenate the string representation of the operands.

  • 整數加法:Integer addition:

    int operator +(int x, int y);
    uint operator +(uint x, uint y);
    long operator +(long x, long y);
    ulong operator +(ulong x, ulong y);
    

    checked 內容中,如果總和超出結果型別的範圍, System.OverflowException 就會擲回。In a checked context, if the sum is outside the range of the result type, a System.OverflowException is thrown. unchecked 內容中,不會報告溢位,而且會捨棄結果型別範圍之外的任何大量高序位位。In an unchecked context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded.

  • 浮點數加法:Floating-point addition:

    float operator +(float x, float y);
    double operator +(double x, double y);
    

    總和是根據 IEEE 754 算術的規則來計算。The sum is computed according to the rules of IEEE 754 arithmetic. 下表列出非零有限值、零、無限大和 NaN 所有可能組合的結果。The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. 在下表中,xy 是非零有限值,而 zx + y 的結果。In the table, x and y are nonzero finite values, and z is the result of x + y. 如果 xy 具有相同的大小,但正負號相反,則 z 為正零。If x and y have the same magnitude but opposite signs, z is positive zero. 如果 x + y 太大而無法在目的地類型中表示, z 則為具有相同正負號的無限大 x + yIf x + y is too large to represent in the destination type, z is an infinity with the same sign as x + y.

    yy +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    xx zz xx xx +inf+inf -inf-inf NaNNaN
    +0+0 yy +0+0 +0+0 +inf+inf -inf-inf NaNNaN
    -0-0 yy +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    +inf+inf +inf+inf +inf+inf +inf+inf +inf+inf NaNNaN NaNNaN
    -inf-inf -inf-inf -inf-inf -inf-inf NaNNaN -inf-inf NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • Decimal 加法:Decimal addition:

    decimal operator +(decimal x, decimal y);
    

    如果產生的值太大而無法以格式表示 decimalSystem.OverflowException 就會擲回。If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. 在任何四捨五入之前,結果的小數位數是兩個運算元的尺規越大。The scale of the result, before any rounding, is the larger of the scales of the two operands.

    Decimal 加法相當於使用類型的加法運算子 System.DecimalDecimal addition is equivalent to using the addition operator of type System.Decimal.

  • 列舉加法。Enumeration addition. 每個列舉型別都會隱含地提供下列預先定義的運算子,其中 E 是列舉型別,而 U 是的基礎型別 EEvery enumeration type implicitly provides the following predefined operators, where E is the enum type, and U is the underlying type of E:

    E operator +(E x, U y);
    E operator +(U x, E y);
    

    在執行時間,這些運算子的評估方式完全相同 (E)((U)x + (U)y)At run-time these operators are evaluated exactly as (E)((U)x + (U)y).

  • 字串串連:String concatenation:

    string operator +(string x, string y);
    string operator +(string x, object y);
    string operator +(object x, string y);
    

    二元運算子的這些多載會 + 執行字串串連。These overloads of the binary + operator perform string concatenation. 如果字串串連的運算元是 null ,則會取代空字串。If an operand of string concatenation is null, an empty string is substituted. 否則,任何非字串引數都會叫用繼承自類型的虛擬方法,以轉換成其字串表示 ToString objectOtherwise, any non-string argument is converted to its string representation by invoking the virtual ToString method inherited from type object. 如果 ToString 傳回 null ,則會取代空字串。If ToString returns null, an empty string is substituted.

    using System;
    
    class Test
    {
        static void Main() {
            string s = null;
            Console.WriteLine("s = >" + s + "<");        // displays s = ><
            int i = 1;
            Console.WriteLine("i = " + i);               // displays i = 1
            float f = 1.2300E+15F;
            Console.WriteLine("f = " + f);               // displays f = 1.23E+15
            decimal d = 2.900m;
            Console.WriteLine("d = " + d);               // displays d = 2.900
        }
    }
    

    字串串連運算子的結果是一個字串,其中包含左運算元的字元以及右運算元的字元。 字串串連運算子永遠不會傳回 null 值。 System.OutOfMemoryException如果沒有足夠的記憶體可配置結果字串,可能會擲回。A System.OutOfMemoryException may be thrown if there is not enough memory available to allocate the resulting string.

  • 委派組合。Delegate combination. 每個委派型別都會隱含地提供下列預先定義的運算子,其中 D 是委派型別:Every delegate type implicitly provides the following predefined operator, where D is the delegate type:

    D operator +(D x, D y);
    

    二元 + 運算子會在兩個運算元都屬於某個委派型別時,執行委派組合 DThe binary + operator performs delegate combination when both operands are of some delegate type D. (如果運算元有不同的委派類型,就會發生系結時期錯誤。 ) 如果第一個運算元是,則作業的 null 結果會是第二個運算元的值 (即使也 null) 。(If the operands have different delegate types, a binding-time error occurs.) If the first operand is null, the result of the operation is the value of the second operand (even if that is also null). 否則,如果第二個運算元是 null ,則運算的結果會是第一個運算元的值。Otherwise, if the second operand is null, then the result of the operation is the value of the first operand. 否則,作業的結果會是新的委派實例,當叫用它時,會叫用第一個運算元,然後叫用第二個運算元。Otherwise, the result of the operation is a new delegate instance that, when invoked, invokes the first operand and then invokes the second operand. 如需委派組合的範例,請參閱 減法運算子委派調用For examples of delegate combination, see Subtraction operator and Delegate invocation. 由於不是 System.Delegate 委派型別, operator  + 因此不會為它定義。Since System.Delegate is not a delegate type, operator + is not defined for it.

減法運算子Subtraction operator

針對格式的作業 x - y ,二元運算子多載解析 (二元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form x - y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

預先定義的減法運算子如下所示。The predefined subtraction operators are listed below. 所有運算子都會減去 y xThe operators all subtract y from x.

  • 整數減法:Integer subtraction:

    int operator -(int x, int y);
    uint operator -(uint x, uint y);
    long operator -(long x, long y);
    ulong operator -(ulong x, ulong y);
    

    checked 內容中,如果差異超出結果型別的範圍, System.OverflowException 就會擲回。In a checked context, if the difference is outside the range of the result type, a System.OverflowException is thrown. unchecked 內容中,不會報告溢位,而且會捨棄結果型別範圍之外的任何大量高序位位。In an unchecked context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded.

  • 浮點數減法:Floating-point subtraction:

    float operator -(float x, float y);
    double operator -(double x, double y);
    

    差異是根據 IEEE 754 算術的規則來計算。The difference is computed according to the rules of IEEE 754 arithmetic. 下表列出所有可能的非零有限值、零、無限大和 Nan 組合的結果。The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaNs. 在下表中,xy 是非零有限值,而 zx - y 的結果。In the table, x and y are nonzero finite values, and z is the result of x - y. 如果 xy 相等,則 z 為正零。If x and y are equal, z is positive zero. 如果 x - y 太大而無法在目的地類型中表示, z 則為具有相同正負號的無限大 x - yIf x - y is too large to represent in the destination type, z is an infinity with the same sign as x - y.

    yy +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    xx zz xx xx -inf-inf +inf+inf NaNNaN
    +0+0 -y-y +0+0 +0+0 -inf-inf +inf+inf NaNNaN
    -0-0 -y-y -0-0 +0+0 -inf-inf +inf+inf NaNNaN
    +inf+inf +inf+inf +inf+inf +inf+inf NaNNaN +inf+inf NaNNaN
    -inf-inf -inf-inf -inf-inf -inf-inf -inf-inf NaNNaN NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • 十進位減法:Decimal subtraction:

    decimal operator -(decimal x, decimal y);
    

    如果產生的值太大而無法以格式表示 decimalSystem.OverflowException 就會擲回。If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. 在任何四捨五入之前,結果的小數位數是兩個運算元的尺規越大。The scale of the result, before any rounding, is the larger of the scales of the two operands.

    Decimal 減法相當於使用類型的減法運算子 System.DecimalDecimal subtraction is equivalent to using the subtraction operator of type System.Decimal.

  • 列舉減法。Enumeration subtraction. 每個列舉型別都會隱含地提供下列預先定義的運算子,其中 E 是列舉型別,而 U 是的基礎型別 EEvery enumeration type implicitly provides the following predefined operator, where E is the enum type, and U is the underlying type of E:

    U operator -(E x, E y);
    

    這個運算子的評估方式完全相同 (U)((U)x - (U)y)This operator is evaluated exactly as (U)((U)x - (U)y). 換句話說,運算子會計算和的序數值之間的差異 x y ,而結果的型別則是列舉的基礎類型。In other words, the operator computes the difference between the ordinal values of x and y, and the type of the result is the underlying type of the enumeration.

    E operator -(E x, U y);
    

    這個運算子的評估方式完全相同 (E)((U)x - y)This operator is evaluated exactly as (E)((U)x - y). 換句話說,運算子會從列舉的基礎類型減去值,並產生列舉的值。In other words, the operator subtracts a value from the underlying type of the enumeration, yielding a value of the enumeration.

  • 委派移除。Delegate removal. 每個委派型別都會隱含地提供下列預先定義的運算子,其中 D 是委派型別:Every delegate type implicitly provides the following predefined operator, where D is the delegate type:

    D operator -(D x, D y);
    

    二元 - 運算子會在兩個運算元都屬於某個委派類型時,執行委派移除 DThe binary - operator performs delegate removal when both operands are of some delegate type D. 如果運算元有不同的委派類型,就會發生系結階段錯誤。If the operands have different delegate types, a binding-time error occurs. 如果第一個運算元是 null,則作業的結果是 nullIf the first operand is null, the result of the operation is null. 否則,如果第二個運算元是 null ,則運算的結果會是第一個運算元的值。Otherwise, if the second operand is null, then the result of the operation is the value of the first operand. 否則,這兩個運算元代表具有一個或多個專案的調用清單 () 委派 宣告,而結果是新的調用清單,其中包含第一個運算元的清單,其中包含第二個運算元的專案,前提是第二個運算元的清單是第一個運算元的適當連續子清單。Otherwise, both operands represent invocation lists (Delegate declarations) having one or more entries, and the result is a new invocation list consisting of the first operand's list with the second operand's entries removed from it, provided the second operand's list is a proper contiguous sublist of the first's. (判斷子清單是否相等,對應的專案會與委派等號比較運算子的比較, (委派等 號比較運算子) ) 。否則,結果就是左運算元的值。(To determine sublist equality, corresponding entries are compared as for the delegate equality operator (Delegate equality operators).) Otherwise, the result is the value of the left operand. 進程中的兩個運算元清單都沒有變更。Neither of the operands' lists is changed in the process. 如果第二個運算元的清單與第一個運算元清單中連續專案的多個清單子相符,則會移除最右邊相符的連續專案子清單。If the second operand's list matches multiple sublists of contiguous entries in the first operand's list, the right-most matching sublist of contiguous entries is removed. 如果移除導致空白清單,則結果是 nullIf removal results in an empty list, the result is null. 例如:For example:

    delegate void D(int x);
    
    class C
    {
        public static void M1(int i) { /* ... */ }
        public static void M2(int i) { /* ... */ }
    }
    
    class Test
    {
        static void Main() { 
            D cd1 = new D(C.M1);
            D cd2 = new D(C.M2);
            D cd3 = cd1 + cd2 + cd2 + cd1;   // M1 + M2 + M2 + M1
            cd3 -= cd1;                      // => M1 + M2 + M2
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd1 + cd2;                // => M2 + M1
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd2 + cd2;                // => M1 + M1
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd2 + cd1;                // => M1 + M2
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd1 + cd1;                // => M1 + M2 + M2 + M1
        }
    }
    

移位運算子Shift operators

<<>> 運算子用來執行位移位作業。The << and >> operators are used to perform bit shifting operations.

shift_expression
    : additive_expression
    | shift_expression '<<' additive_expression
    | shift_expression right_shift additive_expression
    ;

如果 shift_expression 的運算元具有編譯時間型別 dynamic ,則運算式會動態系結 (動態繫結) 。If an operand of a shift_expression has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). 在此情況下,運算式的編譯時間型別是 dynamic ,而且下面所述的解析將會在執行時間使用具有編譯時間類型之運算元的執行時間型別進行 dynamicIn this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

若為表單或的 x << count 運算 x >> count ,二元運算子多載解析 (二元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form x << count or x >> count, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

宣告多載移位運算子時,第一個運算元的類型必須一律為包含運算子宣告的類別或結構,而第二個運算元的類型必須一律為 intWhen declaring an overloaded shift operator, the type of the first operand must always be the class or struct containing the operator declaration, and the type of the second operand must always be int.

預先定義的移位運算子如下所示。The predefined shift operators are listed below.

  • 左移:Shift left:

    int operator <<(int x, int count);
    uint operator <<(uint x, int count);
    long operator <<(long x, int count);
    ulong operator <<(ulong x, int count);
    

    <<運算子會 x 向左移一些計算的位數,如下所述。The << operator shifts x left by a number of bits computed as described below.

    會捨棄結果型別範圍之外的高序位位 x ,其餘的位會左移,而且低序位空白位位置會設定為零。The high-order bits outside the range of the result type of x are discarded, the remaining bits are shifted left, and the low-order empty bit positions are set to zero.

  • 右移:Shift right:

    int operator >>(int x, int count);
    uint operator >>(uint x, int count);
    long operator >>(long x, int count);
    ulong operator >>(ulong x, int count);
    

    >>運算子會 x 由計算的位數右移,如下所述。The >> operator shifts x right by a number of bits computed as described below.

    x 的型別為 int 或時 long ,會捨棄的低序位位 x ,其餘的位會右移,而且如果 x 是負數,則高序位的空白位位置會設定為零 xWhen x is of type int or long, the low-order bits of x are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero if x is non-negative and set to one if x is negative.

    x 的類型為 uint 或時 ulong ,會捨棄的低序位位 x ,其餘的位會右移,而高序位的空白位位置會設定為零。When x is of type uint or ulong, the low-order bits of x are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero.

針對預先定義的運算子,會計算要移位的位數,如下所示:For the predefined operators, the number of bits to shift is computed as follows:

  • 當的型別 xint 或時 uint ,會由的低序位五位指定移位元數目 countWhen the type of x is int or uint, the shift count is given by the low-order five bits of count. 換句話說,會從計算移位元數目 count & 0x1FIn other words, the shift count is computed from count & 0x1F.
  • 當的型別 xlong 或時 ulong ,會由的低序位六位指定移位元數目 countWhen the type of x is long or ulong, the shift count is given by the low-order six bits of count. 換句話說,會從計算移位元數目 count & 0x3FIn other words, the shift count is computed from count & 0x3F.

如果產生的移位元數目為零,則移位運算子只會傳回的值 xIf the resulting shift count is zero, the shift operators simply return the value of x.

移位作業永遠不會造成溢位,並且在和內容中產生相同的結果 checked uncheckedShift operations never cause overflows and produce the same results in checked and unchecked contexts.

當運算子的左運算元 >> 是帶正負號的整數類資料型別時,運算子會對運算元的正負號 (的正負號,將運算元的正負號位) 的值傳播到高序位的空白位位置。When the left operand of the >> operator is of a signed integral type, the operator performs an arithmetic shift right wherein the value of the most significant bit (the sign bit) of the operand is propagated to the high-order empty bit positions. 當運算子的左運算元 >> 是不帶正負號的整數類資料類型時,運算子會執行邏輯右移,其中高序位空白位位置一律設定為零。When the left operand of the >> operator is of an unsigned integral type, the operator performs a logical shift right wherein high-order empty bit positions are always set to zero. 若要執行從運算元類型推斷而來的相反運算,可以使用明確轉換。To perform the opposite operation of that inferred from the operand type, explicit casts can be used. 例如,如果 x 是類型的變數 int ,則作業會 unchecked((int)((uint)x >> y)) 執行的邏輯右移 xFor example, if x is a variable of type int, the operation unchecked((int)((uint)x >> y)) performs a logical shift right of x.

關係和類型測試運算子Relational and type-testing operators

、、、、、 == != < > <= >= isas 運算子稱為關聯式和型別測試運算子。The ==, !=, <, >, <=, >=, is and as operators are called the relational and type-testing operators.

relational_expression
    : shift_expression
    | relational_expression '<' shift_expression
    | relational_expression '>' shift_expression
    | relational_expression '<=' shift_expression
    | relational_expression '>=' shift_expression
    | relational_expression 'is' type
    | relational_expression 'as' type
    ;

equality_expression
    : relational_expression
    | equality_expression '==' relational_expression
    | equality_expression '!=' relational_expression
    ;

is運算子會在 as 運算子描述,而 as 運算子則是在as 運算子中描述。The is operator is described in The is operator and the as operator is described in The as operator.

==、、 != <> <=>= 運算子為 比較運算子The ==, !=, <, >, <= and >= operators are comparison operators.

如果比較運算子的運算元具有編譯時間型別 dynamic ,則運算式會動態繫結 (動態繫結) 。If an operand of a comparison operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). 在此情況下,運算式的編譯時間型別是 dynamic ,而且下面所述的解析將會在執行時間使用具有編譯時間類型之運算元的執行時間型別進行 dynamicIn this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

若為表單 op 的作業 x y ,其中 op 是比較運算子,則會套用多載解析 (二元運算子多載解析) 套用至選取特定的運算子執行。For an operation of the form x op y, where op is a comparison operator, overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

預先定義的比較運算子會在下列各節中說明。The predefined comparison operators are described in the following sections. 所有預先定義的比較運算子都會傳回型別的結果 bool ,如下表所述。All predefined comparison operators return a result of type bool, as described in the following table.

運算Operation 結果Result
x == y true 如果 x 等於,則為 yfalse 否則為。true if x is equal to y, false otherwise
x != y true 如果不 x 等於,則為 yfalse 否則為。true if x is not equal to y, false otherwise
x < y x 小於 y 則為 true;否則為 falsetrue if x is less than y, false otherwise
x > y x 大於 y 則為 true;否則為 falsetrue if x is greater than y, false otherwise
x <= y x 小於或等於 y 則為 true;否則為 falsetrue if x is less than or equal to y, false otherwise
x >= y x 大於或等於 y 則為 true;否則為 falsetrue if x is greater than or equal to y, false otherwise

整數比較運算子Integer comparison operators

預先定義的整數比較運算子如下:The predefined integer comparison operators are:

bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);

bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);

bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);

bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);

bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);

bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);

每個運算子都會比較兩個整數運算元的數值,並傳回 bool 值,指出特定關聯性是 truefalseEach of these operators compares the numeric values of the two integer operands and returns a bool value that indicates whether the particular relation is true or false.

浮點數比較運算子Floating-point comparison operators

預先定義的浮點比較運算子如下:The predefined floating-point comparison operators are:

bool operator ==(float x, float y);
bool operator ==(double x, double y);

bool operator !=(float x, float y);
bool operator !=(double x, double y);

bool operator <(float x, float y);
bool operator <(double x, double y);

bool operator >(float x, float y);
bool operator >(double x, double y);

bool operator <=(float x, float y);
bool operator <=(double x, double y);

bool operator >=(float x, float y);
bool operator >=(double x, double y);

運算子會根據 IEEE 754 標準的規則來比較運算元:The operators compare the operands according to the rules of the IEEE 754 standard:

  • 如果任一個運算元為 NaN,則結果為 false 所有運算子 != ,但結果為 trueIf either operand is NaN, the result is false for all operators except !=, for which the result is true. 針對任何兩個運算元, x != y 一律會產生與相同的結果 !(x == y)For any two operands, x != y always produces the same result as !(x == y). 不過,當一個或兩個運算元都是 NaN 時, <><=>= 運算子不會產生與相反運算子的邏輯否定相同的結果。However, when one or both operands are NaN, the <, >, <=, and >= operators do not produce the same results as the logical negation of the opposite operator. 例如,如果的任一個 x y 是 NaN,則為 x < y false ,但 !(x >= y)trueFor example, if either of x and y is NaN, then x < y is false, but !(x >= y) is true.

  • 當兩個運算元都不是 NaN 時,運算子會比較兩個浮點數運算元的值與順序相關When neither operand is NaN, the operators compare the values of the two floating-point operands with respect to the ordering

    -inf < -max < ... < -min < -0.0 == +0.0 < +min < ... < +max < +inf
    

    其中 minmax 是可依給定浮點數格式表示的最小和最大正有限值。where min and max are the smallest and largest positive finite values that can be represented in the given floating-point format. 此順序值得注意的影響如下:Notable effects of this ordering are:

    • 負零和正零會視為相等。Negative and positive zeros are considered equal.
    • 負無限大會被視為小於所有其他值,但等於另一個負無限大。A negative infinity is considered less than all other values, but equal to another negative infinity.
    • 正無限大視為大於所有其他值,但等於另一個正無限大。A positive infinity is considered greater than all other values, but equal to another positive infinity.

小數比較運算子Decimal comparison operators

預先定義的十進位比較運算子如下:The predefined decimal comparison operators are:

bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);

每個運算子都會比較兩個十進位運算元的數值,並傳回一個 bool 值,指出特定關聯性為 truefalseEach of these operators compares the numeric values of the two decimal operands and returns a bool value that indicates whether the particular relation is true or false. 每個小數比較相當於使用類型的對應關聯式或等號比較運算子 System.DecimalEach decimal comparison is equivalent to using the corresponding relational or equality operator of type System.Decimal.

布林等號比較運算子Boolean equality operators

預先定義的布林相等運算子如下:The predefined boolean equality operators are:

bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);

的結果為,如果和都是 == true x y truex ,而且和 y 都是, false 則為。The result of == is true if both x and y are true or if both x and y are false. 否則,結果為 falseOtherwise, the result is false.

的結果為,如果和都是 != false x y truex ,而且和 y 都是, false 則為。The result of != is false if both x and y are true or if both x and y are false. 否則,結果為 trueOtherwise, the result is true. 當運算元的類型為時 bool!= 運算子會產生與運算子相同的結果 ^When the operands are of type bool, the != operator produces the same result as the ^ operator.

列舉比較運算子Enumeration comparison operators

每個列舉類型都會隱含地提供下列預先定義的比較運算子:Every enumeration type implicitly provides the following predefined comparison operators:

bool operator ==(E x, E y);
bool operator !=(E x, E y);
bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);

評估的結果 x op y (其中 xy 是具有基礎類型之列舉型別的運算式), E Uop 是其中一個比較運算子,與評估完全相同 ((U)x) op ((U)y)The result of evaluating x op y, where x and y are expressions of an enumeration type E with an underlying type U, and op is one of the comparison operators, is exactly the same as evaluating ((U)x) op ((U)y). 換句話說,列舉型別比較運算子只會比較兩個運算元的基礎整數值。In other words, the enumeration type comparison operators simply compare the underlying integral values of the two operands.

參考型別等號比較運算子Reference type equality operators

預先定義的參考型別等號比較運算子為:The predefined reference type equality operators are:

bool operator ==(object x, object y);
bool operator !=(object x, object y);

運算子會傳回比較兩個參考是否相等或不相等的結果。The operators return the result of comparing the two references for equality or non-equality.

由於預先定義的參考型別等號比較運算子接受型別的運算元 object ,因此它們會套用至所有未宣告適用 operator == 和成員的類型 operator !=Since the predefined reference type equality operators accept operands of type object, they apply to all types that do not declare applicable operator == and operator != members. 相反地,任何適用的使用者定義等號比較運算子都會有效地隱藏預先定義的參考型別等號比較運算子。Conversely, any applicable user-defined equality operators effectively hide the predefined reference type equality operators.

預先定義的參考型別等號比較運算子需要下列其中一項:The predefined reference type equality operators require one of the following:

  • 這兩個運算元都是已知為 reference_type 或常值之類型的值 nullBoth operands are a value of a type known to be a reference_type or the literal null. 此外,明確參考轉換 (明確參考 轉換) 存在於任一運算元的類型與另一個運算元的類型之間。Furthermore, an explicit reference conversion (Explicit reference conversions) exists from the type of either operand to the type of the other operand.
  • 其中一個運算元是型別的值, T 其中 Ttype_parameter ,另一個運算元是常值 nullOne operand is a value of type T where T is a type_parameter and the other operand is the literal null. 此外 T ,也沒有實數值型別條件約束。Furthermore T does not have the value type constraint.

除非其中一個條件為 true,否則會發生系結階段錯誤。Unless one of these conditions are true, a binding-time error occurs. 這些規則的顯著含意如下:Notable implications of these rules are:

  • 這是系結階段錯誤,可使用預先定義的參考型別等號比較運算子來比較已知在系結時間不同的兩個參考。It is a binding-time error to use the predefined reference type equality operators to compare two references that are known to be different at binding-time. 例如,如果運算元的系結時間型別是兩個類別型別 AB 而且也不是 A B 衍生自另一個類別,則這兩個運算元將無法參考相同的物件。For example, if the binding-time types of the operands are two class types A and B, and if neither A nor B derives from the other, then it would be impossible for the two operands to reference the same object. 因此,作業會被視為系結階段錯誤。Thus, the operation is considered a binding-time error.
  • 預先定義的參考型別等號比較運算子不允許比較實值型別運算元。The predefined reference type equality operators do not permit value type operands to be compared. 因此,除非結構類型宣告自己的等號比較運算子,否則不可能比較該結構類型的值。Therefore, unless a struct type declares its own equality operators, it is not possible to compare values of that struct type.
  • 預先定義的參考型別等號比較運算子永遠不會導致其運算元發生「裝箱」作業。The predefined reference type equality operators never cause boxing operations to occur for their operands. 執行這類的裝箱作業並不具意義,因為對新配置的已封裝實例的參考必須與其他所有參考不同。It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references.
  • 如果將類型參數類型的運算元 T 與進行比較 null ,且執行時間類型為實 T 值型別,則比較結果為 falseIf an operand of a type parameter type T is compared to null, and the run-time type of T is a value type, the result of the comparison is false.

下列範例會檢查不受限制之類型參數類型的引數是否為 nullThe following example checks whether an argument of an unconstrained type parameter type is null.

class C<T>
{
    void F(T x) {
        if (x == null) throw new ArgumentNullException();
        ...
    }
}

x == null即使可以表示實值型別,也可以使用此結構 T ,而結果只是定義為 falseT 值型別。The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.

若為表單的作業 x == y x != y ,或(如果有任何適用 operator == 或存在),運算子多載 operator != 解析 (二元運算子 多載解析) 規則會選取該運算子,而不是預先定義的參考型別等號比較運算子。For an operation of the form x == y or x != y, if any applicable operator == or operator != exists, the operator overload resolution (Binary operator overload resolution) rules will select that operator instead of the predefined reference type equality operator. 不過,您一律可以明確地將一個或兩個運算元轉換成類型,來選取預先定義的參考型別等號比較運算子 objectHowever, it is always possible to select the predefined reference type equality operator by explicitly casting one or both of the operands to type object. 範例The example

using System;

class Test
{
    static void Main() {
        string s = "Test";
        string t = string.Copy(s);
        Console.WriteLine(s == t);
        Console.WriteLine((object)s == t);
        Console.WriteLine(s == (object)t);
        Console.WriteLine((object)s == (object)t);
    }
}

產生下列輸出produces the output

True
False
False
False

st 變數參考兩個不同 string 的實例,其中包含相同的字元。The s and t variables refer to two distinct string instances containing the same characters. 第一個比較輸出的原因是, True 當兩個運算元的類型為時,會選取預先定義的字串相等運算子 (字串相等運算子) stringThe first comparison outputs True because the predefined string equality operator (String equality operators) is selected when both operands are of type string. 其餘的比較會比較所有輸出 False ,因為當其中一個或兩個運算元的類型為時,會選取預先定義的參考型別等號比較運算子 objectThe remaining comparisons all output False because the predefined reference type equality operator is selected when one or both of the operands are of type object.

請注意,上述技巧對實值型別沒有意義。Note that the above technique is not meaningful for value types. 範例The example

class Test
{
    static void Main() {
        int i = 123;
        int j = 123;
        System.Console.WriteLine((object)i == (object)j);
    }
}

輸出 False ,因為轉換會建立兩個不同的已框值實例的參考 intoutputs False because the casts create references to two separate instances of boxed int values.

字串等號比較運算子String equality operators

預先定義的字串等號比較運算子如下:The predefined string equality operators are:

bool operator ==(string x, string y);
bool operator !=(string x, string y);

string當下列其中一項為真時,兩個值會被視為相等:Two string values are considered equal when one of the following is true:

  • 這兩個值都是 nullBoth values are null.
  • 這兩個值都是在每個字元位置都有相同長度和相同字元之字串實例的非 null 參考。Both values are non-null references to string instances that have identical lengths and identical characters in each character position.

字串等號比較運算子比較字串值,而不是字串參考。The string equality operators compare string values rather than string references. 當兩個不同的字串實例包含完全相同的字元序列時,字串的值會相等,但參考不同。When two separate string instances contain the exact same sequence of characters, the values of the strings are equal, but the references are different. 參考型別等號比較運算子中所述,參考型別等號比較運算子可以用來比較字串參考,而不是字串值。As described in Reference type equality operators, the reference type equality operators can be used to compare string references instead of string values.

委派等號比較運算子Delegate equality operators

每個委派型別都會隱含地提供下列預先定義的比較運算子:Every delegate type implicitly provides the following predefined comparison operators:

bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);

兩個委派實例的視為相同,如下所示:Two delegate instances are considered equal as follows:

  • 如果其中一個委派實例為 null ,則它們都相等,而且只有在兩者皆為時 nullIf either of the delegate instances is null, they are equal if and only if both are null.
  • 如果委派有不同的執行時間類型,它們永遠不會相等。If the delegates have different run-time type they are never equal.
  • 如果這兩個委派實例都有一個調用清單 (委派 宣告) ,則只有在其調用清單的長度相同,且一個調用清單中的每個專案都 (等於對應專案(依序在其他的調用清單中)) 對應專案的情況下,才會相等。If both of the delegate instances have an invocation list (Delegate declarations), those instances are equal if and only if their invocation lists are the same length, and each entry in one's invocation list is equal (as defined below) to the corresponding entry, in order, in the other's invocation list.

下列規則會管理調用清單專案的相等:The following rules govern the equality of invocation list entries:

  • 如果兩個調用清單專案都參考相同的靜態方法,則專案會相等。If two invocation list entries both refer to the same static method then the entries are equal.
  • 如果兩個調用清單專案都參考相同目標物件上的相同非靜態方法 (如參考等號比較運算子所定義) 則專案相等。If two invocation list entries both refer to the same non-static method on the same target object (as defined by the reference equality operators) then the entries are equal.
  • 在以語義相同的 anonymous_method_expression s 或 lambda_expression s 的評估所產生的調用清單專案中,具有相同 (可能會有空白) 的已捕捉外部變數實例集 (但不需要) 相等。Invocation list entries produced from evaluation of semantically identical anonymous_method_expression s or lambda_expression s with the same (possibly empty) set of captured outer variable instances are permitted (but not required) to be equal.

等號比較運算子和 nullEquality operators and null

==And != 運算子允許一個運算元成為可為 null 型別的值,另一個運算元為 null 常值,即使沒有任何預先定義或使用者定義的運算子 (在 unlifted 或提升表單中) 存在於作業中。The == and != operators permit one operand to be a value of a nullable type and the other to be the null literal, even if no predefined or user-defined operator (in unlifted or lifted form) exists for the operation.

針對其中一個表單的操作For an operation of one of the forms

x == null
null == x
x != null
null != x

其中 x 是可為 null 之類型的運算式,如果運算子多載解析 (二元運算子 多載解析) 找不到適用的運算子,則會改為從的屬性計算結果 HasValue xwhere x is an expression of a nullable type, if operator overload resolution (Binary operator overload resolution) fails to find an applicable operator, the result is instead computed from the HasValue property of x. 具體來說,前兩個形式會轉譯成 !x.HasValue ,最後兩個形式會轉譯為 x.HasValueSpecifically, the first two forms are translated into !x.HasValue, and last two forms are translated into x.HasValue.

is 運算子The is operator

is運算子用來動態檢查物件的執行時間類型是否與指定的類型相容。The is operator is used to dynamically check if the run-time type of an object is compatible with a given type. 作業的結果 E is T (其中 E 是運算式且 T 為型別)是一個布林值,指出是否可以透過 E T 參考轉換、裝箱轉換或取消加入的轉換,成功地將轉換成型別。The result of the operation E is T, where E is an expression and T is a type, is a boolean value indicating whether E can successfully be converted to type T by a reference conversion, a boxing conversion, or an unboxing conversion. 在將型別引數替換為所有型別參數之後,此作業會評估如下:The operation is evaluated as follows, after type arguments have been substituted for all type parameters:

  • 如果 E 是匿名函式,就會發生編譯時期錯誤If E is an anonymous function, a compile-time error occurs
  • 如果 E 是方法群組或 null 常值,如果的類型 E 是參考型別或可為 null 的型別,而且的值 E 為 null,則結果為 false。If E is a method group or the null literal, of if the type of E is a reference type or a nullable type and the value of E is null, the result is false.
  • 否則,讓我們 D 以如下方式表示動態類型 EOtherwise, let D represent the dynamic type of E as follows:
    • 如果的型別 E 是參考型別, D 則是實例參考的執行時間型別 EIf the type of E is a reference type, D is the run-time type of the instance reference by E.
    • 如果的型別 E 是可為 null 的型別, D 就是可為 null 型別的基礎型別。If the type of E is a nullable type, D is the underlying type of that nullable type.
    • 如果的型別 E 是不可為 null 的實值型別, D 就是的型別 EIf the type of E is a non-nullable value type, D is the type of E.
  • 作業的結果取決於和,如下所示 D TThe result of the operation depends on D and T as follows:
    • 如果 T 是參考型別,則如果 D 和是 T 相同的型別,而且如果 D 是的參考型別和從的隱含參考轉換 D T ,或者是實值型別, D 而且從轉換 D 成 exists 的 T ,則結果為 true。If T is a reference type, the result is true if D and T are the same type, if D is a reference type and an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists.
    • 如果 T 是可為 null 的型別,則如果 D 是的基礎型別,則結果為 true TIf T is a nullable type, the result is true if D is the underlying type of T.
    • 如果 T 是不可為 null 的實值型別,則如果 D 和是相同的型別,則結果為 true TIf T is a non-nullable value type, the result is true if D and T are the same type.
    • 否則,結果為 false。Otherwise, the result is false.

請注意,運算子不會考慮使用者定義的轉換 isNote that user defined conversions, are not considered by the is operator.

as 運算子The as operator

as運算子用來將值明確轉換為指定的參考型別或可為 null 的型別。The as operator is used to explicitly convert a value to a given reference type or nullable type. 不同于 cast 運算式 (cast 運算式) , as 運算子永遠不會擲回例外狀況。Unlike a cast expression (Cast expressions), the as operator never throws an exception. 相反地,如果無法指定轉換,則產生的值為 nullInstead, if the indicated conversion is not possible, the resulting value is null.

在表單的作業中 E as TE 必須是運算式且 T 必須是參考型別、已知為參考型別的類型參數,或可為 null 的型別。In an operation of the form E as T, E must be an expression and T must be a reference type, a type parameter known to be a reference type, or a nullable type. 此外,至少必須符合下列其中一項條件,否則會發生編譯時期錯誤:Furthermore, at least one of the following must be true, or otherwise a compile-time error occurs:

如果的編譯時間型別 E 不是 dynamic ,則作業 E as T 產生的結果與If the compile-time type of E is not dynamic, the operation E as T produces the same result as

E is T ? (T)(E) : (T)null

但只會評估 E 一次。except that E is only evaluated once. 編譯器可以優化 E as T 為最多執行一次動態類型檢查,而不是上述擴充所隱含的兩個動態類型檢查。The compiler can be expected to optimize E as T to perform at most one dynamic type check as opposed to the two dynamic type checks implied by the expansion above.

如果的編譯時間類型 E 是,則 dynamic 與 cast 運算子不同的是, as 運算子不會動態系結 (動態繫結) 。If the compile-time type of E is dynamic, unlike the cast operator the as operator is not dynamically bound (Dynamic binding). 因此,在此情況下的擴充是:Therefore the expansion in this case is:

E is T ? (T)(object)(E) : (T)null

請注意,某些轉換(例如使用者定義的轉換)無法與運算子搭配 as 使用,而應該改用 cast 運算式來執行。Note that some conversions, such as user defined conversions, are not possible with the as operator and should instead be performed using cast expressions.

在範例中In the example

class X
{

    public string F(object o) {
        return o as string;        // OK, string is a reference type
    }

    public T G<T>(object o) where T: Attribute {
        return o as T;             // Ok, T has a class constraint
    }

    public U H<U>(object o) {
        return o as U;             // Error, U is unconstrained 
    }
}

的型別 T 參數 G 已知為參考型別,因為它具有類別條件約束。the type parameter T of G is known to be a reference type, because it has the class constraint. 但是的型別參數 U H 不是,因此不 as 允許在中使用運算子 HThe type parameter U of H is not however; hence the use of the as operator in H is disallowed.

邏輯運算子Logical operators

&^| 運算子稱為邏輯運算子。The &, ^, and | operators are called the logical operators.

and_expression
    : equality_expression
    | and_expression '&' equality_expression
    ;

exclusive_or_expression
    : and_expression
    | exclusive_or_expression '^' and_expression
    ;

inclusive_or_expression
    : exclusive_or_expression
    | inclusive_or_expression '|' exclusive_or_expression
    ;

如果邏輯運算子的運算元具有編譯時間型別 dynamic ,則運算式會動態繫結 (動態繫結) 。If an operand of a logical operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). 在此情況下,運算式的編譯時間型別是 dynamic ,而且下面所述的解析將會在執行時間使用具有編譯時間類型之運算元的執行時間型別進行 dynamicIn this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

若為表單的作業 x op y (其中 op 是其中一個邏輯運算子),則會套用多載解析 (二元運算子 多載解析) 套用至選取特定的運算子執行。For an operation of the form x op y, where op is one of the logical operators, overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 運算元會轉換成所選運算子的參數類型,而結果的型別則是運算子的傳回型別。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

預先定義的邏輯運算子會在下列各節中說明。The predefined logical operators are described in the following sections.

整數邏輯運算子Integer logical operators

預先定義的整數邏輯運算子為:The predefined integer logical operators are:

int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);

int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);

int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);

&運算子會計算兩個運算元的位邏輯 AND ,運算子會計算兩個運算元的位邏輯 | OR ,而 ^ 運算子會計算兩個運算元的位邏輯互斥 ORThe & operator computes the bitwise logical AND of the two operands, the | operator computes the bitwise logical OR of the two operands, and the ^ operator computes the bitwise logical exclusive OR of the two operands. 這些作業不可能有溢位。No overflows are possible from these operations.

列舉邏輯運算子Enumeration logical operators

每個列舉類型都會 E 隱含地提供下列預先定義的邏輯運算子:Every enumeration type E implicitly provides the following predefined logical operators:

E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);

評估的結果 x op y (其中 xy 是具有基礎類型之列舉類型的運算式), E Uop 是其中一個邏輯運算子,與評估完全相同 (E)((U)x op (U)y)The result of evaluating x op y, where x and y are expressions of an enumeration type E with an underlying type U, and op is one of the logical operators, is exactly the same as evaluating (E)((U)x op (U)y). 換句話說,列舉型別邏輯運算子只會在兩個運算元的基礎類型上執行邏輯運算。In other words, the enumeration type logical operators simply perform the logical operation on the underlying type of the two operands.

布林值邏輯運算子Boolean logical operators

預先定義的布林值邏輯運算子如下:The predefined boolean logical operators are:

bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);

xy 皆為 true,那麼 x & y 的結果會是 trueThe result of x & y is true if both x and y are true. 否則,結果為 falseOtherwise, the result is false.

x | y true 如果 xy 為,則的結果為 trueThe result of x | y is true if either x or y is true. 否則,結果為 falseOtherwise, the result is false.

的結果 x ^ y 為, true 如果 xtrue y ,且為 false ,或者 xfalseytrueThe result of x ^ y is true if x is true and y is false, or x is false and y is true. 否則,結果為 falseOtherwise, the result is false. 當運算元的類型為時 bool^ 運算子會計算與運算子相同的結果 !=When the operands are of type bool, the ^ operator computes the same result as the != operator.

可為 null 的布林值邏輯運算子Nullable boolean logical operators

可為 null 的布林值型別 bool? 可以代表三個值:、 true falsenull ,而且在概念上類似于 SQL 中用於布林運算式的三值型別。The nullable boolean type bool? can represent three values, true, false, and null, and is conceptually similar to the three-valued type used for boolean expressions in SQL. 為了確保和運算元的運算子所產生的結果 & | bool? 與 SQL 的三值邏輯一致,會提供下列預先定義的運算子:To ensure that the results produced by the & and | operators for bool? operands are consistent with SQL's three-valued logic, the following predefined operators are provided:

bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);

下表列出這些運算子針對所有值、和組合所產生的結果 true false nullThe following table lists the results produced by these operators for all combinations of the values true, false, and null.

x y x & y x | y
true true true true
true false false true
true null null true
false true false true
false false false false
false null false null
null true null true
null false false null
null null null null

條件邏輯運算子Conditional logical operators

&&|| 運算子稱為條件邏輯運算子。The && and || operators are called the conditional logical operators. 它們也稱為「短路」邏輯運算子。They are also called the "short-circuiting" logical operators.

conditional_and_expression
    : inclusive_or_expression
    | conditional_and_expression '&&' inclusive_or_expression
    ;

conditional_or_expression
    : conditional_and_expression
    | conditional_or_expression '||' conditional_and_expression
    ;

&&|| 運算子是和運算子的條件式 & 版本 |The && and || operators are conditional versions of the & and | operators:

  • 作業會 x && y 對應至作業 x & y ,但只有在 y 不是時才會評估 x falseThe operation x && y corresponds to the operation x & y, except that y is evaluated only if x is not false.
  • 作業會 x || y 對應至作業 x | y ,但只有在 y 不是時才會評估 x trueThe operation x || y corresponds to the operation x | y, except that y is evaluated only if x is not true.

如果條件式邏輯運算子的運算元具有編譯時間型別,則 dynamic 運算式會動態繫結 (動態繫結) 。If an operand of a conditional logical operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). 在此情況下,運算式的編譯時間型別是 dynamic ,而且下面所述的解析將會在執行時間使用具有編譯時間類型之運算元的執行時間型別進行 dynamicIn this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

表單或的作業會藉由套用多載 x && y x || y 解析 (二元運算子 多載解析) ,如同寫入作業 x & yx | yAn operation of the form x && y or x || y is processed by applying overload resolution (Binary operator overload resolution) as if the operation was written x & y or x | y. 如此一來,Then,

  • 如果多載解析找不到單一最佳運算子,或多載解析選取其中一個預先定義的整數邏輯運算子,則會發生系結階段錯誤。If overload resolution fails to find a single best operator, or if overload resolution selects one of the predefined integer logical operators, a binding-time error occurs.
  • 否則,如果選取的運算子是其中一個預先定義的布林值邏輯運算子 (布林值邏輯 運算子) 或可為 null 的布林值邏輯運算子 (可為 null 的布林值邏輯運算子) ,則會如 布林條件邏輯運算子中所述處理作業。Otherwise, if the selected operator is one of the predefined boolean logical operators (Boolean logical operators) or nullable boolean logical operators (Nullable boolean logical operators), the operation is processed as described in Boolean conditional logical operators.
  • 否則,選取的運算子是使用者定義的運算子,且作業會依照 使用者定義的條件式邏輯運算子中的說明進行處理。Otherwise, the selected operator is a user-defined operator, and the operation is processed as described in User-defined conditional logical operators.

您無法直接多載條件式邏輯運算子。It is not possible to directly overload the conditional logical operators. 不過,由於條件式邏輯運算子是以一般邏輯運算子來評估,因此一般邏輯運算子的多載會有某些限制,也會視為條件式邏輯運算子的多載。However, because the conditional logical operators are evaluated in terms of the regular logical operators, overloads of the regular logical operators are, with certain restrictions, also considered overloads of the conditional logical operators. 這會在 使用者定義的條件式邏輯運算子中進一步說明。This is described further in User-defined conditional logical operators.

布林條件邏輯運算子Boolean conditional logical operators

當或的運算元 && 屬於 || 型別時 bool ,或當運算元的類型不定義適用的 operator &operator | (但是定義了隱含轉換為)時,作業 bool 的處理方式如下:When the operands of && or || are of type bool, or when the operands are of types that do not define an applicable operator & or operator |, but do define implicit conversions to bool, the operation is processed as follows:

  • 運算 x && y 會評估為 x ? y : falseThe operation x && y is evaluated as x ? y : false. 換句話說, x 會先進行評估,並轉換成型別 boolIn other words, x is first evaluated and converted to type bool. 然後,如果 xtruey 則會評估並轉換成類型 bool ,而這會成為作業的結果。Then, if x is true, y is evaluated and converted to type bool, and this becomes the result of the operation. 否則,作業的結果為 falseOtherwise, the result of the operation is false.
  • 運算 x || y 會評估為 x ? true : yThe operation x || y is evaluated as x ? true : y. 換句話說, x 會先進行評估,並轉換成型別 boolIn other words, x is first evaluated and converted to type bool. 然後,如果 x 是,則作業的 true 結果為 trueThen, if x is true, the result of the operation is true. 否則, y 會進行評估並轉換成類型 bool ,而這會成為作業的結果。Otherwise, y is evaluated and converted to type bool, and this becomes the result of the operation.

使用者定義條件式邏輯運算子User-defined conditional logical operators

當或的運算元屬於宣告 && || 適用使用者定義或的型別時 operator & operator | ,下列兩項都必須為 true,其中是所 T 選運算子的宣告類型:When the operands of && or || are of types that declare an applicable user-defined operator & or operator |, both of the following must be true, where T is the type in which the selected operator is declared:

  • 所選運算子的傳回類型和每個參數的類型必須是 TThe return type and the type of each parameter of the selected operator must be T. 換句話說,運算子必須計算 AND 兩個型別運算元的邏輯 or 邏輯 OR T ,而且必須傳回型別的結果 TIn other words, the operator must compute the logical AND or the logical OR of two operands of type T, and must return a result of type T.
  • T 必須包含和的 operator true 宣告 operator falseT must contain declarations of operator true and operator false.

如果未滿足任何一項需求,便會發生系結階段錯誤。A binding-time error occurs if either of these requirements is not satisfied. 否則, && 會將 || 使用者定義的 operator trueoperator false 與選取的使用者定義運算子結合,來評估 or 運算:Otherwise, the && or || operation is evaluated by combining the user-defined operator true or operator false with the selected user-defined operator:

  • 作業 x && y 會評估為 T.false(x) ? x : T.&(x, y) ,其中 T.false(x) 是在中宣告的調用, operator false T 而且 T.&(x, y) 是所選的調用 operator &The operation x && y is evaluated as T.false(x) ? x : T.&(x, y), where T.false(x) is an invocation of the operator false declared in T, and T.&(x, y) is an invocation of the selected operator &. 換句話說, x 會先進行評估,並 operator false 在結果上叫用,以判斷是否 x 肯定為 false。In other words, x is first evaluated and operator false is invoked on the result to determine if x is definitely false. 然後,如果 x 肯定為 false,則作業的結果會是先前針對所計算的值 xThen, if x is definitely false, the result of the operation is the value previously computed for x. 否則, y 會進行評估,並 operator & 在先前計算的值上叫用選取的值, x 並針對所計算的值,以產生作業的 y 結果。Otherwise, y is evaluated, and the selected operator & is invoked on the value previously computed for x and the value computed for y to produce the result of the operation.
  • 作業 x || y 會評估為 T.true(x) ? x : T.|(x, y) ,其中 T.true(x) 是在中宣告的調用, operator true T 而且 T.|(x,y) 是所選的調用 operator|The operation x || y is evaluated as T.true(x) ? x : T.|(x, y), where T.true(x) is an invocation of the operator true declared in T, and T.|(x,y) is an invocation of the selected operator|. 換句話說, x 會先進行評估,並 operator true 在結果上叫用,以判斷是否 x 為絕對 true。In other words, x is first evaluated and operator true is invoked on the result to determine if x is definitely true. 然後,如果 x 肯定是 true,則作業的結果會是先前針對所計算的值 xThen, if x is definitely true, the result of the operation is the value previously computed for x. 否則, y 會進行評估,並 operator | 在先前計算的值上叫用選取的值, x 並針對所計算的值,以產生作業的 y 結果。Otherwise, y is evaluated, and the selected operator | is invoked on the value previously computed for x and the value computed for y to produce the result of the operation.

在這些作業中,指定的運算式 x 只會評估一次,而指定的運算式 y 則不會只評估或評估一次。In either of these operations, the expression given by x is only evaluated once, and the expression given by y is either not evaluated or evaluated exactly once.

如需實和的型別範例 operator true operator false ,請參閱 資料庫布林值型別。For an example of a type that implements operator true and operator false, see Database boolean type.

Null 聯合運算子The null coalescing operator

??運算子稱為 null 聯合運算子。The ?? operator is called the null coalescing operator.

null_coalescing_expression
    : conditional_or_expression
    | conditional_or_expression '??' null_coalescing_expression
    ;

表單的 null 聯合運算式 a ?? b a 必須是可為 null 的型別或參考型別。A null coalescing expression of the form a ?? b requires a to be of a nullable type or reference type. 如果 a 為非 null,則的結果為 a ?? ba 否則結果為 bIf a is non-null, the result of a ?? b is a; otherwise, the result is b. b只有當為 null 時,作業才 a 會評估。The operation evaluates b only if a is null.

Null 聯合運算子是右向關聯的,表示作業是由右至左分組。The null coalescing operator is right-associative, meaning that operations are grouped from right to left. 例如,表單的運算式 a ?? b ?? c 會評估為 a ?? (b ?? c)For example, an expression of the form a ?? b ?? c is evaluated as a ?? (b ?? c). 一般情況下,表單的運算式 E1 ?? E2 ?? ... ?? En 會傳回非 null 的第一個運算元,如果所有運算元都是 null,則為 null。In general terms, an expression of the form E1 ?? E2 ?? ... ?? En returns the first of the operands that is non-null, or null if all operands are null.

運算式的類型 a ?? b 取決於運算元上可用的隱含轉換。The type of the expression a ?? b depends on which implicit conversions are available on the operands. 依喜好設定,的型別 a ?? bA0AB ,其中 A 是提供類型為) 之 (的型別 a ,是指提供型別) 的 a B (型別 b b ,而且 A0 A 如果 A 是可為 null 的型別,則為的基礎型別, A 否則為。In order of preference, the type of a ?? b is A0, A, or B, where A is the type of a (provided that a has a type), B is the type of b (provided that b has a type), and A0 is the underlying type of A if A is a nullable type, or A otherwise. 具體而言, a ?? b 會以下列方式處理:Specifically, a ?? b is processed as follows:

  • 如果 A 存在,而且不是可為 null 的型別或參考型別,就會發生編譯時期錯誤。If A exists and is not a nullable type or a reference type, a compile-time error occurs.
  • 如果 b 是動態運算式,則結果類型為 dynamicIf b is a dynamic expression, the result type is dynamic. 在執行時間, a 會先評估。At run-time, a is first evaluated. 如果不 a 是 null, a 則會轉換為動態,而這會成為結果。If a is not null, a is converted to dynamic, and this becomes the result. 否則, b 會進行評估,而這會成為結果。Otherwise, b is evaluated, and this becomes the result.
  • 否則,如果 A 存在,而且是可為 null 的型別,而且有隱含轉換 b A0 ,則結果類型為 A0Otherwise, if A exists and is a nullable type and an implicit conversion exists from b to A0, the result type is A0. 在執行時間, a 會先評估。At run-time, a is first evaluated. 如果不 a 是 null, a 則會解除包裝為類型 A0 ,而這會成為結果。If a is not null, a is unwrapped to type A0, and this becomes the result. 否則, b 會進行評估並轉換成類型 A0 ,而這會成為結果。Otherwise, b is evaluated and converted to type A0, and this becomes the result.
  • 否則,如果 A 存在,而且有隱含轉換 b ,則 A 結果類型為 AOtherwise, if A exists and an implicit conversion exists from b to A, the result type is A. 在執行時間, a 會先評估。At run-time, a is first evaluated. 如果不 a 是 null, a 就會成為結果。If a is not null, a becomes the result. 否則, b 會進行評估並轉換成類型 A ,而這會成為結果。Otherwise, b is evaluated and converted to type A, and this becomes the result.
  • 否則,如果的 b 類型 B 和隱含轉換存在 aB ,則結果類型為 BOtherwise, if b has a type B and an implicit conversion exists from a to B, the result type is B. 在執行時間, a 會先評估。At run-time, a is first evaluated. 如果不 a 是 null, a 則會將解除包裝為類型 A0 (如果存在,而且可為 A null) 並且轉換為類型,則會 B 變成結果。If a is not null, a is unwrapped to type A0 (if A exists and is nullable) and converted to type B, and this becomes the result. 否則, b 會進行評估並成為結果。Otherwise, b is evaluated and becomes the result.
  • 否則, ab 不相容,而且會發生編譯時期錯誤。Otherwise, a and b are incompatible, and a compile-time error occurs.

條件運算子Conditional operator

?:運算子稱為條件運算子。The ?: operator is called the conditional operator. 它有時也稱為三元運算子。It is at times also called the ternary operator.

conditional_expression
    : null_coalescing_expression
    | null_coalescing_expression '?' expression ':' expression
    ;

表單的條件運算式會 b ? x : y 先評估條件 bA conditional expression of the form b ? x : y first evaluates the condition b. 然後,如果 btruex 則會評估,並且成為作業的結果。Then, if b is true, x is evaluated and becomes the result of the operation. 否則, y 會進行評估,並成為作業的結果。Otherwise, y is evaluated and becomes the result of the operation. 條件運算式永遠不會評估 xyA conditional expression never evaluates both x and y.

條件運算子是右向關聯的,表示作業是由右至左分組。The conditional operator is right-associative, meaning that operations are grouped from right to left. 例如,表單的運算式 a ? b : c ? d : e 會評估為 a ? b : (c ? d : e)For example, an expression of the form a ? b : c ? d : e is evaluated as a ? b : (c ? d : e).

運算子的第一個運算元 ?: 必須是可以隱含地轉換為的運算式 bool ,或是實作為型別的運算式 operator trueThe first operand of the ?: operator must be an expression that can be implicitly converted to bool, or an expression of a type that implements operator true. 如果未滿足上述任何一項需求,就會發生編譯時期錯誤。If neither of these requirements is satisfied, a compile-time error occurs.

運算子的第二個和第三個運算元 x y ?: 控制條件運算式的型別。The second and third operands, x and y, of the ?: operator control the type of the conditional expression.

  • 如果 x 有型別 X ,而且 y 有型別 Y thenIf x has type X and y has type Y then
    • 如果隱含轉換 (隱含 轉換) 存在於 XY ,而不是從轉換為,則 Y X Y 是條件運算式的類型。If an implicit conversion (Implicit conversions) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
    • 如果隱含轉換 (隱含 轉換) 存在於 YX ,而不是從轉換為,則 X Y X 是條件運算式的類型。If an implicit conversion (Implicit conversions) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
    • 否則,就不能判斷任何運算式型別,而且會發生編譯時期錯誤。Otherwise, no expression type can be determined, and a compile-time error occurs.
  • 如果只有一個 xy 具有型別,而且和兩者都可以隱含地 x y 轉換成該型別,則這是條件運算式的型別。If only one of x and y has a type, and both x and y, of are implicitly convertible to that type, then that is the type of the conditional expression.
  • 否則,就不能判斷任何運算式型別,而且會發生編譯時期錯誤。Otherwise, no expression type can be determined, and a compile-time error occurs.

表單之條件運算式的執行時間處理是 b ? x : y 由下列步驟所組成:The run-time processing of a conditional expression of the form b ? x : y consists of the following steps:

  • 首先, b 會進行評估,並 bool 決定的值 bFirst, b is evaluated, and the bool value of b is determined:
    • 如果從的型別隱含轉換 b bool 存在,則會執行這項隱含轉換來產生 bool 值。If an implicit conversion from the type of b to bool exists, then this implicit conversion is performed to produce a bool value.
    • 否則,會叫用的型別所 operator true 定義的 b 來產生 bool 值。Otherwise, the operator true defined by the type of b is invoked to produce a bool value.
  • 如果 bool 上述步驟所產生的值為 true ,則 x 會進行評估並轉換成條件運算式的類型,而這會成為條件運算式的結果。If the bool value produced by the step above is true, then x is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression.
  • 否則, y 會進行評估並轉換成條件運算式的類型,而這會成為條件運算式的結果。Otherwise, y is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression.

匿名函式運算式Anonymous function expressions

匿名 函式是表示「內嵌」方法定義的運算式。An anonymous function is an expression that represents an "in-line" method definition. 匿名函式本身沒有值或型別,但是可以轉換成相容的委派或運算式樹狀架構型別。An anonymous function does not have a value or type in and of itself, but is convertible to a compatible delegate or expression tree type. 匿名函式轉換的評估取決於轉換的目標型別:如果它是委派型別,則轉換會評估為參考匿名函式所定義之方法的委派值。The evaluation of an anonymous function conversion depends on the target type of the conversion: If it is a delegate type, the conversion evaluates to a delegate value referencing the method which the anonymous function defines. 如果是運算式樹狀架構型別,則轉換會評估為運算式樹狀架構,它會將方法的結構表示為物件結構。If it is an expression tree type, the conversion evaluates to an expression tree which represents the structure of the method as an object structure.

基於歷史原因,匿名函式有兩種語法類別,也就是 lambda_expression s 和 anonymous_method_expression s。For historical reasons there are two syntactic flavors of anonymous functions, namely lambda_expression s and anonymous_method_expression s. 對於幾乎所有的目的而言, lambda_expression s 比 anonymous_method_expression 的更為精簡且易懂,後者仍會保留為回溯相容性的語言。For almost all purposes, lambda_expression s are more concise and expressive than anonymous_method_expression s, which remain in the language for backwards compatibility.

lambda_expression
    : anonymous_function_signature '=>' anonymous_function_body
    ;

anonymous_method_expression
    : 'delegate' explicit_anonymous_function_signature? block
    ;

anonymous_function_signature
    : explicit_anonymous_function_signature
    | implicit_anonymous_function_signature
    ;

explicit_anonymous_function_signature
    : '(' explicit_anonymous_function_parameter_list? ')'
    ;

explicit_anonymous_function_parameter_list
    : explicit_anonymous_function_parameter (',' explicit_anonymous_function_parameter)*
    ;

explicit_anonymous_function_parameter
    : anonymous_function_parameter_modifier? type identifier
    ;

anonymous_function_parameter_modifier
    : 'ref'
    | 'out'
    ;

implicit_anonymous_function_signature
    : '(' implicit_anonymous_function_parameter_list? ')'
    | implicit_anonymous_function_parameter
    ;

implicit_anonymous_function_parameter_list
    : implicit_anonymous_function_parameter (',' implicit_anonymous_function_parameter)*
    ;

implicit_anonymous_function_parameter
    : identifier
    ;

anonymous_function_body
    : expression
    | block
    ;

=>運算子的優先順序與指派 (=) 相同,而且是右向關聯。The => operator has the same precedence as assignment (=) and is right-associative.

具有修飾詞的匿名函式 async 是非同步函式,並遵循 非同步函式中所述的規則。An anonymous function with the async modifier is an async function and follows the rules described in Async functions.

匿名函式的參數(以 lambda_expression 的形式)可以明確或隱含的型別。The parameters of an anonymous function in the form of a lambda_expression can be explicitly or implicitly typed. 在明確類型的參數清單中,會明確陳述每個參數的型別。In an explicitly typed parameter list, the type of each parameter is explicitly stated. 在隱含型別參數清單中,參數的型別是從發生匿名函式的內容推斷而來的,尤其是當匿名函式轉換成相容的委派型別或運算式樹狀架構型別時,該型別會提供參數類型 (匿名函式 轉換) 。In an implicitly typed parameter list, the types of the parameters are inferred from the context in which the anonymous function occurs—specifically, when the anonymous function is converted to a compatible delegate type or expression tree type, that type provides the parameter types (Anonymous function conversions).

在具有單一隱含類型參數的匿名函式中,可以省略參數清單中的括弧。In an anonymous function with a single, implicitly typed parameter, the parentheses may be omitted from the parameter list. 換句話說,這是表單的匿名函式In other words, an anonymous function of the form

( param ) => expr

可以縮寫為can be abbreviated to

param => expr

匿名函式的參數清單(以 anonymous_method_expression 的形式)是選擇性的。The parameter list of an anonymous function in the form of an anonymous_method_expression is optional. 如果有指定,則必須明確地輸入參數。If given, the parameters must be explicitly typed. 如果沒有,匿名函式可以轉換成具有任何參數清單(不含參數)的委派 outIf not, the anonymous function is convertible to a delegate with any parameter list not containing out parameters.

匿名函式的 區塊 主體可 (端點和 可存取性) 連接,除非匿名函式發生在無法連接的語句內。A block body of an anonymous function is reachable (End points and reachability) unless the anonymous function occurs inside an unreachable statement.

匿名函式的一些範例如下:Some examples of anonymous functions follow below:

x => x + 1                              // Implicitly typed, expression body
x => { return x + 1; }                  // Implicitly typed, statement body
(int x) => x + 1                        // Explicitly typed, expression body
(int x) => { return x + 1; }            // Explicitly typed, statement body
(x, y) => x * y                         // Multiple parameters
() => Console.WriteLine()               // No parameters
async (t1,t2) => await t1 + await t2    // Async
delegate (int x) { return x + 1; }      // Anonymous method expression
delegate { return 1 + 1; }              // Parameter list omitted

除了下列幾點之外, lambda_expression s 和 anonymous_method_expression s 的行為相同:The behavior of lambda_expression s and anonymous_method_expression s is the same except for the following points:

  • anonymous_method_expression 允許完全省略參數清單,產生可轉換性來委派任何值參數清單的類型。anonymous_method_expression s permit the parameter list to be omitted entirely, yielding convertibility to delegate types of any list of value parameters.
  • lambda_expression 允許省略和推斷參數類型,而 anonymous_method_expression s 則需要明確陳述參數類型。lambda_expression s permit parameter types to be omitted and inferred whereas anonymous_method_expression s require parameter types to be explicitly stated.
  • Lambda_expression 的主體可以是運算式或語句區塊,而 anonymous_method_expression 的主體必須是語句區塊。The body of a lambda_expression can be an expression or a statement block whereas the body of an anonymous_method_expression must be a statement block.
  • 只有 lambda_expression s 會轉換成相容的運算式樹狀架構類型 (運算式樹狀架構類型) 。Only lambda_expression s have conversions to compatible expression tree types (Expression tree types).

匿名函式簽章Anonymous function signatures

匿名函式的選擇性 anonymous_function_signature 定義匿名函式的名稱,以及選擇性的型式參數類型。The optional anonymous_function_signature of an anonymous function defines the names and optionally the types of the formal parameters for the anonymous function. 匿名函式的參數範圍是 anonymous_function_bodyThe scope of the parameters of the anonymous function is the anonymous_function_body. (範圍) 和參數清單 ((如果指定的話)) 匿名方法主體會構成宣告空間 (宣告) 。(Scopes) Together with the parameter list (if given) the anonymous-method-body constitutes a declaration space (Declarations). 因此,匿名函式之參數名稱的編譯時期錯誤,會比對其範圍包含 anonymous_method_expressionlambda_expression 的區域變數、本機常數或參數的名稱。It is thus a compile-time error for the name of a parameter of the anonymous function to match the name of a local variable, local constant or parameter whose scope includes the anonymous_method_expression or lambda_expression.

如果匿名函式有 explicit_anonymous_function_signature,則相容委派型別和運算式樹狀架構類型的集合,會限制為具有相同順序的相同參數類型和修飾詞。If an anonymous function has an explicit_anonymous_function_signature, then the set of compatible delegate types and expression tree types is restricted to those that have the same parameter types and modifiers in the same order. 相較于方法群組轉換 (方法群組轉換) ,不支援匿名函式參數類型的不變變異數。In contrast to method group conversions (Method group conversions), contra-variance of anonymous function parameter types is not supported. 如果匿名函式沒有 anonymous_function_signature,則會將一組相容的委派型別和運算式樹狀架構型別限制為沒有參數的類型 outIf an anonymous function does not have an anonymous_function_signature, then the set of compatible delegate types and expression tree types is restricted to those that have no out parameters.

請注意, anonymous_function_signature 不能包含屬性或參數陣列。Note that an anonymous_function_signature cannot include attributes or a parameter array. 然而, anonymous_function_signature 可能與參數清單包含參數陣列的委派型別相容。Nevertheless, an anonymous_function_signature may be compatible with a delegate type whose parameter list contains a parameter array.

另外也請注意,即使相容,轉換成運算式樹狀架構類型仍可能在編譯時期 (運算式樹狀結構類型) 時失敗。Note also that conversion to an expression tree type, even if compatible, may still fail at compile-time (Expression tree types).

匿名函數主體Anonymous function bodies

匿名函式 (運算式區塊) 主體受限於下列規則:The body (expression or block) of an anonymous function is subject to the following rules:

  • 如果匿名函式包含簽章,則簽章中指定的參數會出現在主體中。If the anonymous function includes a signature, the parameters specified in the signature are available in the body. 如果匿名函式沒有簽章,則可以將參數轉換成具有參數 (匿名函式 轉換) 的委派類型或運算式類型,但無法在主體中存取參數。If the anonymous function has no signature it can be converted to a delegate type or expression type having parameters (Anonymous function conversions), but the parameters cannot be accessed in the body.
  • 除了簽 refout 中指定的或參數 (如果最接近的封入匿名函式有任何) ,就會發生編譯時期錯誤,主體才可存取 refout 參數。Except for ref or out parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a ref or out parameter.
  • 當的型別 this 是結構類型時,就是要存取主體的編譯時期錯誤 thisWhen the type of this is a struct type, it is a compile-time error for the body to access this. 無論存取是否明確 (如 this.x) 或隱含 (一樣,例如 x where x 是結構) 的實例成員。This is true whether the access is explicit (as in this.x) or implicit (as in x where x is an instance member of the struct). 此規則只會禁止這類存取,而且不會影響成員查閱結果是否在結構的成員中。This rule simply prohibits such access and does not affect whether member lookup results in a member of the struct.
  • 主體可存取匿名函式 (外部 變數) 外部變數。The body has access to the outer variables (Outer variables) of the anonymous function. 外部變數的存取權將會參考 lambda_expressionanonymous_method_expression (評估時使用中的變數實例,) 匿名函數運算式的評估Access of an outer variable will reference the instance of the variable that is active at the time the lambda_expression or anonymous_method_expression is evaluated (Evaluation of anonymous function expressions).
  • 這是一個編譯時期錯誤,主體包含的 goto 語句、 break 語句或 continue 語句的目標是在主體外部,或在包含的匿名函式主體內。It is a compile-time error for the body to contain a goto statement, break statement, or continue statement whose target is outside the body or within the body of a contained anonymous function.
  • return主體中的語句會從最接近的封入匿名函式的調用傳回控制權,而不是從封入函數成員傳回。A return statement in the body returns control from an invocation of the nearest enclosing anonymous function, not from the enclosing function member. 語句中指定的運算式 return 必須可以隱含地轉換成委派型別或運算式樹狀結構型別的傳回型別,也就是最接近的封入 lambda_expressionanonymous_method_expression 轉換 (匿名函數轉換) 。An expression specified in a return statement must be implicitly convertible to the return type of the delegate type or expression tree type to which the nearest enclosing lambda_expression or anonymous_method_expression is converted (Anonymous function conversions).

無論是否有任何方法可以執行匿名函式的區塊,而不是透過 lambda_expressionanonymous_method_expression 的評估和調用,也會明確指定。It is explicitly unspecified whether there is any way to execute the block of an anonymous function other than through evaluation and invocation of the lambda_expression or anonymous_method_expression. 尤其是,編譯器可能會選擇藉由合成一或多個命名方法或類型來執行匿名函式。In particular, the compiler may choose to implement an anonymous function by synthesizing one or more named methods or types. 任何這類合成元素的名稱都必須是保留供編譯器使用的表單。The names of any such synthesized elements must be of a form reserved for compiler use.

多載解析和匿名函數Overload resolution and anonymous functions

引數清單中的匿名函式會參與型別推斷及多載解析。Anonymous functions in an argument list participate in type inference and overload resolution. 請參閱確切規則的 型別推斷 和多載 解析Please refer to Type inference and Overload resolution for the exact rules.

下列範例說明匿名函式對多載解析的影響。The following example illustrates the effect of anonymous functions on overload resolution.

class ItemList<T>: List<T>
{
    public int Sum(Func<T,int> selector) {
        int sum = 0;
        foreach (T item in this) sum += selector(item);
        return sum;
    }

    public double Sum(Func<T,double> selector) {
        double sum = 0;
        foreach (T item in this) sum += selector(item);
        return sum;
    }
}

ItemList<T>類別有兩種 Sum 方法。The ItemList<T> class has two Sum methods. 每個都採用 selector 引數,它會從清單專案中解壓縮值以加總。Each takes a selector argument, which extracts the value to sum over from a list item. 解壓縮的值可以是 intdouble ,而且產生的總和同樣是 intdoubleThe extracted value can be either an int or a double and the resulting sum is likewise either an int or a double.

例如,您 Sum 可以使用方法,根據訂單中的詳細資料行清單來計算總和。The Sum methods could for example be used to compute sums from a list of detail lines in an order.

class Detail
{
    public int UnitCount;
    public double UnitPrice;
    ...
}

void ComputeSums() {
    ItemList<Detail> orderDetails = GetOrderDetails(...);
    int totalUnits = orderDetails.Sum(d => d.UnitCount);
    double orderTotal = orderDetails.Sum(d => d.UnitPrice * d.UnitCount);
    ...
}

在的第一個調用中 orderDetails.Sum ,這兩種 Sum 方法都適用,因為匿名函式 d => d. UnitCount 與和都相容 Func<Detail,int> Func<Detail,double>In the first invocation of orderDetails.Sum, both Sum methods are applicable because the anonymous function d => d. UnitCount is compatible with both Func<Detail,int> and Func<Detail,double>. 不過,多載解析會挑選第一個 Sum 方法,因為轉換成 Func<Detail,int> 優於轉換成 Func<Detail,double>However, overload resolution picks the first Sum method because the conversion to Func<Detail,int> is better than the conversion to Func<Detail,double>.

在的第二個調用中 orderDetails.Sum ,只有第二個 Sum 方法適用,因為匿名函式會 d => d.UnitPrice * d.UnitCount 產生類型的值 doubleIn the second invocation of orderDetails.Sum, only the second Sum method is applicable because the anonymous function d => d.UnitPrice * d.UnitCount produces a value of type double. 因此,多載解析會挑選該調用的第二個 Sum 方法。Thus, overload resolution picks the second Sum method for that invocation.

匿名函式和動態繫結Anonymous functions and dynamic binding

匿名函式不可以是動態繫結作業的接收者、引數或運算元。An anonymous function cannot be a receiver, argument or operand of a dynamically bound operation.

外部變數Outer variables

任何區域變數、值參數或其範圍包含 lambda_expressionanonymous_method_expression 的參數陣列,稱為匿名函式的 外部變數Any local variable, value parameter, or parameter array whose scope includes the lambda_expression or anonymous_method_expression is called an outer variable of the anonymous function. 在類別的實例函式成員中, this 值會被視為值參數,而且是包含在函數成員內之任何匿名函式的外部變數。In an instance function member of a class, the this value is considered a value parameter and is an outer variable of any anonymous function contained within the function member.

已捕捉的外部變數Captured outer variables

匿名函式參考外部變數時,會將外部變數稱為匿名函式所 捕捉When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. 一般來說,本機變數的存留期僅限於執行與 (區域變數) 相關聯的區塊或語句。Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (Local variables). 不過,在從匿名函式建立的委派或運算式樹狀結構變成適合進行垃圾收集之前,已捕捉的外部變數存留期至少會延伸。However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.

在範例中In the example

using System;

delegate int D();

class Test
{
    static D F() {
        int x = 0;
        D result = () => ++x;
        return result;
    }

    static void Main() {
        D d = F();
        Console.WriteLine(d());
        Console.WriteLine(d());
        Console.WriteLine(d());
    }
}

區域變數 x 是由匿名函式所捕捉,而且的存留期 x 至少要等到從傳回的委派 F 變成合格的垃圾收集 (才會發生,直到程式的最後面) 為止。the local variable x is captured by the anonymous function, and the lifetime of x is extended at least until the delegate returned from F becomes eligible for garbage collection (which doesn't happen until the very end of the program). 由於匿名函式的每個調用都是在相同的實例上運作 x ,因此範例的輸出為:Since each invocation of the anonymous function operates on the same instance of x, the output of the example is:

1
2
3

匿名函式捕捉到區域變數或值參數時,就不會再將區域變數或參數視為固定變數 (固定和可移動變數) ,但會被視為可移動變數。When a local variable or a value parameter is captured by an anonymous function, the local variable or parameter is no longer considered to be a fixed variable (Fixed and moveable variables), but is instead considered to be a moveable variable. 因此 unsafe ,任何採用已捕捉外部變數位址的程式碼,都必須先使用 fixed 語句來修正變數。Thus any unsafe code that takes the address of a captured outer variable must first use the fixed statement to fix the variable.

請注意,不同于 uncaptured 變數,已捕捉的區域變數可以同時公開給多個執行執行緒。Note that unlike an uncaptured variable, a captured local variable can be simultaneously exposed to multiple threads of execution.

區域變數的具現化Instantiation of local variables

當執行進入變數的範圍時,就會將本機變數視為具現 A local variable is considered to be instantiated when execution enters the scope of the variable. 例如,叫用下列方法時,會將區域變數 x 具現化並初始化三次,每次反覆運算迴圈。For example, when the following method is invoked, the local variable x is instantiated and initialized three times—once for each iteration of the loop.

static void F() {
    for (int i = 0; i < 3; i++) {
        int x = i * 2 + 1;
        ...
    }
}

不過,移動迴圈外的宣告會產生的單一具現 xxHowever, moving the declaration of x outside the loop results in a single instantiation of x:

static void F() {
    int x;
    for (int i = 0; i < 3; i++) {
        x = i * 2 + 1;
        ...
    }
}

如果未捕獲,就無法確切觀察區域變數的具現化頻率(因為具現化的存留期不相鄰),因此每個具現化都可能只使用相同的儲存位置。When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantiation to simply use the same storage location. 不過,當匿名函式捕捉到區域變數時,具現化的效果會變得很明顯。However, when an anonymous function captures a local variable, the effects of instantiation become apparent.

範例The example

using System;

delegate void D();

class Test
{
    static D[] F() {
        D[] result = new D[3];
        for (int i = 0; i < 3; i++) {
            int x = i * 2 + 1;
            result[i] = () => { Console.WriteLine(x); };
        }
        return result;
    }

    static void Main() {
        foreach (D d in F()) d();
    }
}

產生下列輸出:produces the output:

1
3
5

但是,當的宣告 x 移至迴圈外時:However, when the declaration of x is moved outside the loop:

static D[] F() {
    D[] result = new D[3];
    int x;
    for (int i = 0; i < 3; i++) {
        x = i * 2 + 1;
        result[i] = () => { Console.WriteLine(x); };
    }
    return result;
}

輸出為:the output is:

5
5
5

如果 for 迴圈宣告了反覆運算變數,則會將該變數本身視為在迴圈外部宣告。If a for-loop declares an iteration variable, that variable itself is considered to be declared outside of the loop. 因此,如果範例變更為捕捉反覆運算變數本身:Thus, if the example is changed to capture the iteration variable itself:

static D[] F() {
    D[] result = new D[3];
    for (int i = 0; i < 3; i++) {
        result[i] = () => { Console.WriteLine(i); };
    }
    return result;
}

只會捕獲一個反覆運算變數的實例,這會產生輸出:only one instance of the iteration variable is captured, which produces the output:

3
3
3

匿名函式委派有可能共用部分已捕捉的變數,但有不同的其他實例。It is possible for anonymous function delegates to share some captured variables yet have separate instances of others. 例如,如果 F 變更為For example, if F is changed to

static D[] F() {
    D[] result = new D[3];
    int x = 0;
    for (int i = 0; i < 3; i++) {
        int y = 0;
        result[i] = () => { Console.WriteLine("{0} {1}", ++x, ++y); };
    }
    return result;
}

這三個委派會捕捉相同的實例 x ,但不同的實例 y ,輸出為:the three delegates capture the same instance of x but separate instances of y, and the output is:

1 1
2 1
3 1

不同的匿名函式可以捕獲外部變數的相同實例。Separate anonymous functions can capture the same instance of an outer variable. 在範例中︰In the example:

using System;

delegate void Setter(int value);

delegate int Getter();

class Test
{
    static void Main() {
        int x = 0;
        Setter s = (int value) => { x = value; };
        Getter g = () => { return x; };
        s(5);
        Console.WriteLine(g());
        s(10);
        Console.WriteLine(g());
    }
}

這兩個匿名函式會捕捉相同的本機變數實例 x ,因此可以透過該變數「通訊」。the two anonymous functions capture the same instance of the local variable x, and they can thus "communicate" through that variable. 範例的輸出如下:The output of the example is:

5
10

匿名函式運算式的評估Evaluation of anonymous function expressions

匿名函式 F 必須一律轉換成委派類型 D 或運算式樹狀結構類型 E ,不論是直接或透過執行委派建立運算式 new D(F)An anonymous function F must always be converted to a delegate type D or an expression tree type E, either directly or through the execution of a delegate creation expression new D(F). 這項轉換會決定匿名函式的結果,如 匿名函數轉換中所述。This conversion determines the result of the anonymous function, as described in Anonymous function conversions.

查詢運算式Query expressions

查詢運算式 會針對類似關聯式和階層式查詢語言的查詢(例如 SQL 和 XQuery)提供語言整合語法。Query expressions provide a language integrated syntax for queries that is similar to relational and hierarchical query languages such as SQL and XQuery.

query_expression
    : from_clause query_body
    ;

from_clause
    : 'from' type? identifier 'in' expression
    ;

query_body
    : query_body_clauses? select_or_group_clause query_continuation?
    ;

query_body_clauses
    : query_body_clause
    | query_body_clauses query_body_clause
    ;

query_body_clause
    : from_clause
    | let_clause
    | where_clause
    | join_clause
    | join_into_clause
    | orderby_clause
    ;

let_clause
    : 'let' identifier '=' expression
    ;

where_clause
    : 'where' boolean_expression
    ;

join_clause
    : 'join' type? identifier 'in' expression 'on' expression 'equals' expression
    ;

join_into_clause
    : 'join' type? identifier 'in' expression 'on' expression 'equals' expression 'into' identifier
    ;

orderby_clause
    : 'orderby' orderings
    ;

orderings
    : ordering (',' ordering)*
    ;

ordering
    : expression ordering_direction?
    ;

ordering_direction
    : 'ascending'
    | 'descending'
    ;

select_or_group_clause
    : select_clause
    | group_clause
    ;

select_clause
    : 'select' expression
    ;

group_clause
    : 'group' expression 'by' expression
    ;

query_continuation
    : 'into' identifier query_body
    ;

查詢運算式是以子句開頭 from ,並以 select or group 子句結尾。A query expression begins with a from clause and ends with either a select or group clause. 初始 from 子句後面可以是零或多個 fromletwhere joinorderby 子句。The initial from clause can be followed by zero or more from, let, where, join or orderby clauses. 每個 from 子句都是引入 *範圍變數 _ 的產生器,其範圍是 _ sequence * 的元素。Each from clause is a generator introducing a range variable _ which ranges over the elements of a _sequence**. 每個子句都會導入 let 一個範圍變數,代表以先前的範圍變數所計算的值。Each let clause introduces a range variable representing a value computed by means of previous range variables. 每個 where 子句都是從結果中排除專案的篩選準則。Each where clause is a filter that excludes items from the result. 每個 join 子句都會比較來源序列的指定索引鍵與另一個序列的索引鍵,以產生相符的配對。Each join clause compares specified keys of the source sequence with keys of another sequence, yielding matching pairs. 每個 orderby 子句會根據指定的準則重新排序專案。Final selectgroup 子句會根據範圍變數來指定結果的形狀。Each orderby clause reorders items according to specified criteria.The final select or group clause specifies the shape of the result in terms of the range variables. 最後, into 子句可以用來「拼接」查詢,方法是將某個查詢的結果視為後續查詢中的產生器。Finally, an into clause can be used to "splice" queries by treating the results of one query as a generator in a subsequent query.

查詢運算式中的多義性Ambiguities in query expressions

查詢運算式包含許多「內容關鍵字」,也就是在給定內容中具有特殊意義的識別碼。Query expressions contain a number of "contextual keywords", i.e., identifiers that have special meaning in a given context. 具體而言,就是、、、、、、、、、、 from where join on equals into let orderby ascending descending select groupbySpecifically these are from, where, join, on, equals, into, let, orderby, ascending, descending, select, group and by. 為了避免由於將這些識別碼混用為關鍵字或簡單名稱而造成的查詢運算式不明確,在查詢運算式中的任何位置進行這些識別碼時,這些識別碼都會被視為關鍵字。In order to avoid ambiguities in query expressions caused by mixed use of these identifiers as keywords or simple names, these identifiers are considered keywords when occurring anywhere within a query expression.

基於這個目的,查詢運算式是以 " from identifier " 後面接著任何標記("" ; ," = " 或 "" 除外)開頭的任何運算式 ,For this purpose, a query expression is any expression that starts with "from identifier" followed by any token except ";", "=" or ",".

若要在查詢運算式中使用這些字組作為識別碼,可以在前面加上 " @ " (識別碼) 。In order to use these words as identifiers within a query expression, they can be prefixed with "@" (Identifiers).

查詢運算式轉譯Query expression translation

C # 語言不會指定查詢運算式的執行語法。The C# language does not specify the execution semantics of query expressions. 相反地,查詢運算式會轉譯成符合 查詢運算式模式 的方法調用, (查詢運算式模式) 。Rather, query expressions are translated into invocations of methods that adhere to the query expression pattern (The query expression pattern). 具體而言,查詢運算式會轉譯為名為 Where 、、 Select SelectManyJoinGroupJoin OrderBy OrderByDescending ThenBy ThenByDescending GroupBy Cast 、、、、、和的方法調用。這些方法預期會有特定的簽章和結果類型,如 查詢運算式模式中所述。Specifically, query expressions are translated into invocations of methods named Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupBy, and Cast.These methods are expected to have particular signatures and result types, as described in The query expression pattern. 這些方法可以是所查詢之物件的實例方法或物件外部的擴充方法,而且它們會執行查詢的實際執行。These methods can be instance methods of the object being queried or extension methods that are external to the object, and they implement the actual execution of the query.

從查詢運算式到方法調用的轉譯是在執行任何類型系結或多載解析之前發生的語法對應。The translation from query expressions to method invocations is a syntactic mapping that occurs before any type binding or overload resolution has been performed. 轉譯保證語法正確,但不保證會產生語義正確的 c # 程式碼。The translation is guaranteed to be syntactically correct, but it is not guaranteed to produce semantically correct C# code. 在查詢運算式的轉譯之後,會將產生的方法叫用視為一般方法調用來處理,而這可能會導致發現錯誤,例如,如果方法不存在、引數有錯誤的型別,或者方法是泛型,且型別推斷失敗。Following translation of query expressions, the resulting method invocations are processed as regular method invocations, and this may in turn uncover errors, for example if the methods do not exist, if arguments have wrong types, or if the methods are generic and type inference fails.

藉由重複套用下列轉譯來處理查詢運算式,直到無法進一步降低。A query expression is processed by repeatedly applying the following translations until no further reductions are possible. 轉譯會依應用程式順序列出:每一節假設先前章節中的翻譯都已徹底執行,一旦完成後,就不會在處理相同的查詢運算式時,再進行一次。The translations are listed in order of application: each section assumes that the translations in the preceding sections have been performed exhaustively, and once exhausted, a section will not later be revisited in the processing of the same query expression.

查詢運算式中不允許指派給範圍變數。Assignment to range variables is not allowed in query expressions. 不過,c # 的執行不一定會強制執行這項限制,因為這裡所提供的語法轉譯配置有時可能無法這麼做。However a C# implementation is permitted to not always enforce this restriction, since this may sometimes not be possible with the syntactic translation scheme presented here.

某些翻譯插入範圍變數,並以表示的透明識別碼表示 *Certain translations inject range variables with transparent identifiers denoted by *. 透明識別碼的特殊屬性會在 透明識別碼中進一步討論。The special properties of transparent identifiers are discussed further in Transparent identifiers.

具有接續的 Select 和 groupby 子句Select and groupby clauses with continuations

具有接續的查詢運算式A query expression with a continuation

from ... into x ...

會轉譯成is translated into

from x in ( from ... ) ...

下列各節中的翻譯假設查詢沒有接續 intoThe translations in the following sections assume that queries have no into continuations.

範例The example

from c in customers
group c by c.Country into g
select new { Country = g.Key, CustCount = g.Count() }

會轉譯成is translated into

from g in
    from c in customers
    group c by c.Country
select new { Country = g.Key, CustCount = g.Count() }

最終的翻譯是the final translation of which is

customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Count() })

明確範圍變數類型Explicit range variable types

from明確指定範圍變數類型的子句。A from clause that explicitly specifies a range variable type

from T x in e

會轉譯成is translated into

from x in ( e ) . Cast < T > ( )

join明確指定範圍變數類型的子句。A join clause that explicitly specifies a range variable type

join T x in e on k1 equals k2

會轉譯成is translated into

join x in ( e ) . Cast < T > ( ) on k1 equals k2

下列各節中的翻譯假設查詢沒有明確的範圍變數類型。The translations in the following sections assume that queries have no explicit range variable types.

範例The example

from Customer c in customers
where c.City == "London"
select c

會轉譯成is translated into

from c in customers.Cast<Customer>()
where c.City == "London"
select c

最終的翻譯是the final translation of which is

customers.
Cast<Customer>().
Where(c => c.City == "London")

明確範圍變數型別有助於查詢實作為非泛型介面的集合 IEnumerable ,而不是泛型 IEnumerable<T> 介面。Explicit range variable types are useful for querying collections that implement the non-generic IEnumerable interface, but not the generic IEnumerable<T> interface. 在上述範例中,如果的型別為,就會發生這種情況 customers ArrayListIn the example above, this would be the case if customers were of type ArrayList.

退化查詢運算式Degenerate query expressions

表單的查詢運算式A query expression of the form

from x in e select x

會轉譯成is translated into

( e ) . Select ( x => x )

範例The example

from c in customers
select c

會轉譯成is translated into

customers.Select(c => c)

退化查詢運算式是完整選取來源元素的運算式。A degenerate query expression is one that trivially selects the elements of the source. 轉譯的後續階段會移除其他轉譯步驟所引進的退化查詢,方法是將它們取代為其來源。A later phase of the translation removes degenerate queries introduced by other translation steps by replacing them with their source. 不過,請務必確定查詢運算式的結果永遠不是來源物件本身,因為這樣會顯示查詢用戶端的來源型別和身分識別。It is important however to ensure that the result of a query expression is never the source object itself, as that would reveal the type and identity of the source to the client of the query. 因此,此步驟會在來源上明確呼叫,以保護直接在原始程式碼中撰寫的退化查詢 SelectTherefore this step protects degenerate queries written directly in source code by explicitly calling Select on the source. 然後由 Select 和其他查詢運算子的實作者來確保這些方法永遠不會傳回來源物件本身。It is then up to the implementers of Select and other query operators to ensure that these methods never return the source object itself.

From、let、where、join 和 orderby 子句From, let, where, join and orderby clauses

具有第二個 from 子句後面接著子句的查詢 select 運算式A query expression with a second from clause followed by a select clause

from x1 in e1
from x2 in e2
select v

會轉譯成is translated into

( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )

具有第二個子句的查詢運算式,後面接著子句以外的某個部分 from selectA query expression with a second from clause followed by something other than a select clause:

from x1 in e1
from x2 in e2
...

會轉譯成is translated into

from * in ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 } )
...

具有子句的查詢運算式 letA query expression with a let clause

from x in e
let y = f
...

會轉譯成is translated into

from * in ( e ) . Select ( x => new { x , y = f } )
...

具有子句的查詢運算式 whereA query expression with a where clause

from x in e
where f
...

會轉譯成is translated into

from x in ( e ) . Where ( x => f )
...

具有 join 子句且 into 後面沒有 select 子句的查詢運算式A query expression with a join clause without an into followed by a select clause

from x1 in e1
join x2 in e2 on k1 equals k2
select v

會轉譯成is translated into

( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v )

具有子句的查詢運算式, join 但後面沒有子句以外的 into 其他內容 selectA query expression with a join clause without an into followed by something other than a select clause

from x1 in e1
join x2 in e2 on k1 equals k2
...

會轉譯成is translated into

from * in ( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 })
...

具有 join 子句 into 後面接著 select 子句的查詢運算式A query expression with a join clause with an into followed by a select clause

from x1 in e1
join x2 in e2 on k1 equals k2 into g
select v

會轉譯成is translated into

( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )

具有子句的查詢運算式, join into 後面接著子句以外的其他內容 selectA query expression with a join clause with an into followed by something other than a select clause

from x1 in e1
join x2 in e2 on k1 equals k2 into g
...

會轉譯成is translated into

from * in ( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
...

具有子句的查詢運算式 orderbyA query expression with an orderby clause

from x in e
orderby k1 , k2 , ..., kn
...

會轉譯成is translated into

from x in ( e ) . 
OrderBy ( x => k1 ) . 
ThenBy ( x => k2 ) .
... .
ThenBy ( x => kn )
...

如果順序子句指定 descending 方向指標,則 OrderByDescending ThenByDescending 會改為產生或的調用。If an ordering clause specifies a descending direction indicator, an invocation of OrderByDescending or ThenByDescending is produced instead.

下列轉譯假設沒有 letwhere joinorderby 子句,而且 from 每個查詢運算式中不能有一個以上的初始子句。The following translations assume that there are no let, where, join or orderby clauses, and no more than the one initial from clause in each query expression.

範例The example

from c in customers
from o in c.Orders
select new { c.Name, o.OrderID, o.Total }

會轉譯成is translated into

customers.
SelectMany(c => c.Orders,
     (c,o) => new { c.Name, o.OrderID, o.Total }
)

範例The example

from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

會轉譯成is translated into

from * in customers.
    SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

最終的翻譯是the final translation of which is

customers.
SelectMany(c => c.Orders, (c,o) => new { c, o }).
OrderByDescending(x => x.o.Total).
Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })

其中 x 是編譯器產生的識別碼,否則不可見和無法存取。where x is a compiler generated identifier that is otherwise invisible and inaccessible.

範例The example

from o in orders
let t = o.Details.Sum(d => d.UnitPrice * d.Quantity)
where t >= 1000
select new { o.OrderID, Total = t }

會轉譯成is translated into

from * in orders.
    Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
where t >= 1000 
select new { o.OrderID, Total = t }

最終的翻譯是the final translation of which is

orders.
Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }).
Where(x => x.t >= 1000).
Select(x => new { x.o.OrderID, Total = x.t })

其中 x 是編譯器產生的識別碼,否則不可見和無法存取。where x is a compiler generated identifier that is otherwise invisible and inaccessible.

範例The example

from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total }

會轉譯成is translated into

customers.Join(orders, c => c.CustomerID, o => o.CustomerID,
    (c, o) => new { c.Name, o.OrderDate, o.Total })

範例The example

from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

會轉譯成is translated into

from * in customers.
    GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,
        (c, co) => new { c, co })
let n = co.Count()
where n >= 10 
select new { c.Name, OrderCount = n }

最終的翻譯是the final translation of which is

customers.
GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,
    (c, co) => new { c, co }).
Select(x => new { x, n = x.co.Count() }).
Where(y => y.n >= 10).
Select(y => new { y.x.c.Name, OrderCount = y.n)

其中 xy 是編譯器產生的識別碼,在其他情況下不可見和無法存取。where x and y are compiler generated identifiers that are otherwise invisible and inaccessible.

範例The example

from o in orders
orderby o.Customer.Name, o.Total descending
select o

具有最終翻譯has the final translation

orders.
OrderBy(o => o.Customer.Name).
ThenByDescending(o => o.Total)

Select 子句Select clauses

表單的查詢運算式A query expression of the form

from x in e select v

會轉譯成is translated into

( e ) . Select ( x => v )

除非 v 是識別碼 x,否則翻譯只是except when v is the identifier x, the translation is simply

( e )

例如:For example

from c in customers.Where(c => c.City == "London")
select c

只會轉譯成is simply translated into

customers.Where(c => c.City == "London")

Groupby 子句Groupby clauses

表單的查詢運算式A query expression of the form

from x in e group v by k

會轉譯成is translated into

( e ) . GroupBy ( x => k , x => v )

除非 v 是識別碼 x,否則翻譯為except when v is the identifier x, the translation is

( e ) . GroupBy ( x => k )

範例The example

from c in customers
group c.Name by c.Country

會轉譯成is translated into

customers.
GroupBy(c => c.Country, c => c.Name)

透明識別碼Transparent identifiers

某些翻譯插入範圍變數,其中包含由表示的 *透明識別碼 _ _Certain translations inject range variables with *transparent identifiers _ denoted by _. 透明識別碼不是正確的語言功能;它們只會以中繼步驟的形式存在於查詢運算式轉譯流程中。Transparent identifiers are not a proper language feature; they exist only as an intermediate step in the query expression translation process.

當查詢轉譯插入透明識別碼時,進一步的轉譯步驟會將透明識別碼傳播至匿名函式和匿名物件初始化運算式。When a query translation injects a transparent identifier, further translation steps propagate the transparent identifier into anonymous functions and anonymous object initializers. 在這些內容中,透明識別碼具有下列行為:In those contexts, transparent identifiers have the following behavior:

  • 當透明識別碼以匿名函式中的參數形式出現時,關聯匿名型別的成員會自動在匿名函數主體的範圍內。When a transparent identifier occurs as a parameter in an anonymous function, the members of the associated anonymous type are automatically in scope in the body of the anonymous function.
  • 當具有透明識別碼的成員在範圍內時,該成員的成員也會在範圍中。When a member with a transparent identifier is in scope, the members of that member are in scope as well.
  • 當透明識別碼以匿名物件初始化運算式中的成員宣告子形式出現時,會引入具有透明識別碼的成員。When a transparent identifier occurs as a member declarator in an anonymous object initializer, it introduces a member with a transparent identifier.
  • 在上述的轉譯步驟中,透明的識別碼一律會與匿名型別一起導入,而意圖是將多個範圍變數當作單一物件的成員來捕捉。In the translation steps described above, transparent identifiers are always introduced together with anonymous types, with the intent of capturing multiple range variables as members of a single object. C # 的執行允許使用與匿名型別不同的機制,將多個範圍變數群組在一起。An implementation of C# is permitted to use a different mechanism than anonymous types to group together multiple range variables. 下列轉譯範例假設使用匿名型別,並顯示透明識別碼如何轉譯。The following translation examples assume that anonymous types are used, and show how transparent identifiers can be translated away.

範例The example

from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.Total }

會轉譯成is translated into

from * in customers.
    SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.Total }

這會進一步轉譯為which is further translated into

customers.
SelectMany(c => c.Orders, (c,o) => new { c, o }).
OrderByDescending(* => o.Total).
Select(* => new { c.Name, o.Total })

當您清除透明識別碼時,就相當於which, when transparent identifiers are erased, is equivalent to

customers.
SelectMany(c => c.Orders, (c,o) => new { c, o }).
OrderByDescending(x => x.o.Total).
Select(x => new { x.c.Name, x.o.Total })

其中 x 是編譯器產生的識別碼,否則不可見和無法存取。where x is a compiler generated identifier that is otherwise invisible and inaccessible.

範例The example

from c in customers
join o in orders on c.CustomerID equals o.CustomerID
join d in details on o.OrderID equals d.OrderID
join p in products on d.ProductID equals p.ProductID
select new { c.Name, o.OrderDate, p.ProductName }

會轉譯成is translated into

from * in customers.
    Join(orders, c => c.CustomerID, o => o.CustomerID, 
        (c, o) => new { c, o })
join d in details on o.OrderID equals d.OrderID
join p in products on d.ProductID equals p.ProductID
select new { c.Name, o.OrderDate, p.ProductName }

這會進一步縮減為which is further reduced to

customers.
Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }).
Join(details, * => o.OrderID, d => d.OrderID, (*, d) => new { *, d }).
Join(products, * => d.ProductID, p => p.ProductID, (*, p) => new { *, p }).
Select(* => new { c.Name, o.OrderDate, p.ProductName })

最終的翻譯是the final translation of which is

customers.
Join(orders, c => c.CustomerID, o => o.CustomerID,
    (c, o) => new { c, o }).
Join(details, x => x.o.OrderID, d => d.OrderID,
    (x, d) => new { x, d }).
Join(products, y => y.d.ProductID, p => p.ProductID,
    (y, p) => new { y, p }).
Select(z => new { z.y.x.c.Name, z.y.x.o.OrderDate, z.p.ProductName })

其中 xyz 是編譯器產生的識別碼,在其他情況下不可見且無法存取。where x, y, and z are compiler generated identifiers that are otherwise invisible and inaccessible.

查詢運算式模式The query expression pattern

查詢運算式模式 會建立方法的模式,這些方法可供型別執行以支援查詢運算式。The Query expression pattern establishes a pattern of methods that types can implement to support query expressions. 由於查詢運算式會藉由語法對應轉譯為方法調用,因此類型在實作為查詢運算式模式的方式上具有相當大的彈性。Because query expressions are translated to method invocations by means of a syntactic mapping, types have considerable flexibility in how they implement the query expression pattern. 例如,模式的方法可以實作為實例方法或擴充方法,因為這兩個具有相同的調用語法,而且方法可以要求委派或運算式樹狀架構,因為匿名函式可以轉換為兩者。For example, the methods of the pattern can be implemented as instance methods or as extension methods because the two have the same invocation syntax, and the methods can request delegates or expression trees because anonymous functions are convertible to both.

支援查詢運算式模式之泛型型別的建議圖形 C<T> 如下所示。The recommended shape of a generic type C<T> that supports the query expression pattern is shown below. 泛型型別是用來說明參數和結果型別之間的正確關聯性,但是也可以針對非泛型型別執行模式。A generic type is used in order to illustrate the proper relationships between parameter and result types, but it is possible to implement the pattern for non-generic types as well.

delegate R Func<T1,R>(T1 arg1);

delegate R Func<T1,T2,R>(T1 arg1, T2 arg2);

class C
{
    public C<T> Cast<T>();
}

class C<T> : C
{
    public C<T> Where(Func<T,bool> predicate);

    public C<U> Select<U>(Func<T,U> selector);

    public C<V> SelectMany<U,V>(Func<T,C<U>> selector,
        Func<T,U,V> resultSelector);

    public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
        Func<U,K> innerKeySelector, Func<T,U,V> resultSelector);

    public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
        Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector);

    public O<T> OrderBy<K>(Func<T,K> keySelector);

    public O<T> OrderByDescending<K>(Func<T,K> keySelector);

    public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector);

    public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector,
        Func<T,E> elementSelector);
}

class O<T> : C<T>
{
    public O<T> ThenBy<K>(Func<T,K> keySelector);

    public O<T> ThenByDescending<K>(Func<T,K> keySelector);
}

class G<K,T> : C<T>
{
    public K Key { get; }
}

上述方法會使用泛型委派型別 Func<T1,R>Func<T1,T2,R> ,不過它們也可以在參數和結果型別中使用具有相同關聯性的其他委派或運算式樹狀結構類型。The methods above use the generic delegate types Func<T1,R> and Func<T1,T2,R>, but they could equally well have used other delegate or expression tree types with the same relationships in parameter and result types.

請注意和之間的建議關聯性, C<T> O<T> 這可確保 ThenByThenByDescending 方法只能在或的結果上 OrderBy 使用 OrderByDescendingNotice the recommended relationship between C<T> and O<T> which ensures that the ThenBy and ThenByDescending methods are available only on the result of an OrderBy or OrderByDescending. 另請注意結果的建議圖形 GroupBy --一連串序列,其中每個內部序列都有一個額外的 Key 屬性。Also notice the recommended shape of the result of GroupBy -- a sequence of sequences, where each inner sequence has an additional Key property.

System.Linq命名空間會為實介面的任何型別提供查詢運算子模式的實作為 System.Collections.Generic.IEnumerable<T>The System.Linq namespace provides an implementation of the query operator pattern for any type that implements the System.Collections.Generic.IEnumerable<T> interface.

指派運算子Assignment operators

指派運算子會將新值指派給變數、屬性、事件或索引子元素。The assignment operators assign a new value to a variable, a property, an event, or an indexer element.

assignment
    : unary_expression assignment_operator expression
    ;

assignment_operator
    : '='
    | '+='
    | '-='
    | '*='
    | '/='
    | '%='
    | '&='
    | '|='
    | '^='
    | '<<='
    | right_shift_assignment
    ;

指派的左運算元必須是分類為變數、屬性存取、索引子存取或事件存取的運算式。The left operand of an assignment must be an expression classified as a variable, a property access, an indexer access, or an event access.

=運算子稱為 簡單指派運算子The = operator is called the simple assignment operator. 它會將右運算元的值指派給左運算元所指定的變數、屬性或索引子元素。It assigns the value of the right operand to the variable, property, or indexer element given by the left operand. 簡單指派運算子的左運算元可能不是事件存取 (但如 類似欄位的事件) 中所述。The left operand of the simple assignment operator may not be an event access (except as described in Field-like events). 簡單指派運算子會在 簡單指派中描述。The simple assignment operator is described in Simple assignment.

運算子以外的指派運算子 = 稱為 複合指派運算子The assignment operators other than the = operator are called the compound assignment operators. 這些運算子會在兩個運算元上執行指定的運算,然後將產生的值指派給左運算元所指定的變數、屬性或索引子元素。These operators perform the indicated operation on the two operands, and then assign the resulting value to the variable, property, or indexer element given by the left operand. 複合指派運算子會在 複合指派中描述。The compound assignment operators are described in Compound assignment.

+= -= 使用事件存取運算式作為左運算元的和運算子,稱為 事件指派運算子The += and -= operators with an event access expression as the left operand are called the event assignment operators. 沒有其他指派運算子適用于以事件存取作為左運算元。No other assignment operator is valid with an event access as the left operand. 事件指派運算子會在 事件指派中描述。The event assignment operators are described in Event assignment.

指派運算子是右向關聯的,表示作業是由右至左分組。The assignment operators are right-associative, meaning that operations are grouped from right to left. 例如,表單的運算式 a = b = c 會評估為 a = (b = c)For example, an expression of the form a = b = c is evaluated as a = (b = c).

單一指派Simple assignment

=運算子稱為簡單指派運算子。The = operator is called the simple assignment operator.

如果簡單指派的左運算元是表單 E.PE[Ei] E 具有編譯時間類型的 dynamic ,則指派會動態繫結 (動態繫結) 。If the left operand of a simple assignment is of the form E.P or E[Ei] where E has the compile-time type dynamic, then the assignment is dynamically bound (Dynamic binding). 在此情況下,指派運算式的編譯時間類型是 dynamic ,而且下面所述的解析將會在執行時間根據的執行時間類型進行 EIn this case the compile-time type of the assignment expression is dynamic, and the resolution described below will take place at run-time based on the run-time type of E.

在簡單的指派中,右運算元必須是可隱含轉換成左運算元類型的運算式。In a simple assignment, the right operand must be an expression that is implicitly convertible to the type of the left operand. 作業會將右運算元的值指派給左運算元所指定的變數、屬性或索引子元素。The operation assigns the value of the right operand to the variable, property, or indexer element given by the left operand.

簡單指派運算式的結果是指派給左運算元的值。The result of a simple assignment expression is the value assigned to the left operand. 結果與左運算元的類型相同,而且一律分類為值。The result has the same type as the left operand and is always classified as a value.

如果左運算元是屬性或索引子存取,則屬性或索引子必須有 set 存取子。If the left operand is a property or indexer access, the property or indexer must have a set accessor. 如果不是這種情況,就會發生系結階段錯誤。If this is not the case, a binding-time error occurs.

簡單指派表單的執行時間處理是 x = y 由下列步驟所組成:The run-time processing of a simple assignment of the form x = y consists of the following steps:

  • 如果 x 分類為變數:If x is classified as a variable:
    • x 會評估以產生變數。x is evaluated to produce the variable.
    • y 會進行評估,並視需要透過隱含轉換轉換成的類型 x (隱含 轉換) 。y is evaluated and, if required, converted to the type of x through an implicit conversion (Implicit conversions).
    • 如果所指定的變數 xreference_type 的陣列元素,則會執行執行時間檢查,以確保計算的值 y 與的陣列實例相容,其 x 為元素。If the variable given by x is an array element of a reference_type, a run-time check is performed to ensure that the value computed for y is compatible with the array instance of which x is an element. 如果 ynull ,或如果隱含參考轉換 (隱含參考 轉換) 存在於所參考之 y 陣列實例的實際元素類型中,則檢查會成功 xThe check succeeds if y is null, or if an implicit reference conversion (Implicit reference conversions) exists from the actual type of the instance referenced by y to the actual element type of the array instance containing x. 否則會擲回 System.ArrayTypeMismatchExceptionOtherwise, a System.ArrayTypeMismatchException is thrown.
    • 評估和轉換所產生的值 y 會儲存至評估所指定的位置 xThe value resulting from the evaluation and conversion of y is stored into the location given by the evaluation of x.
  • 如果 x 分類為屬性或索引子存取:If x is classified as a property or indexer access:
    • 如果不是) ,則為實例運算式 (如果 x static 是,則為引數清單 (如果 x 是,則會評估與相關聯的索引子存取) x ,而且結果會用於後續的 set 存取子調用。The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent set accessor invocation.
    • y 會進行評估,並視需要透過隱含轉換轉換成的類型 x (隱含 轉換) 。y is evaluated and, if required, converted to the type of x through an implicit conversion (Implicit conversions).
    • set 存取子 x 會以計算的值 y 做為其引數來叫用 valueThe set accessor of x is invoked with the value computed for y as its value argument.

陣列共變數規則 (陣列共 變數) 允許陣列類型的值 A[] 為數組類型實例的參考 B[] ,但前提是已有隱含的參考轉換 B AThe array co-variance rules (Array covariance) permit a value of an array type A[] to be a reference to an instance of an array type B[], provided an implicit reference conversion exists from B to A. 因為這些規則,指派給 reference_type 的陣列元素需要執行時間檢查,以確保所指派的值與陣列實例相容。Because of these rules, assignment to an array element of a reference_type requires a run-time check to ensure that the value being assigned is compatible with the array instance. 在範例中In the example

string[] sa = new string[10];
object[] oa = sa;

oa[0] = null;               // Ok
oa[1] = "Hello";            // Ok
oa[2] = new ArrayList();    // ArrayTypeMismatchException

最後一個指派會導致擲回 System.ArrayTypeMismatchException ,因為 ArrayList 無法將實例儲存在的元素中 string[]the last assignment causes a System.ArrayTypeMismatchException to be thrown because an instance of ArrayList cannot be stored in an element of a string[].

struct_type 中宣告的屬性或索引子是指派的目標時,與屬性或索引子存取相關聯的實例運算式必須分類為變數。When a property or indexer declared in a struct_type is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. 如果實例運算式分類為值,就會發生系結階段錯誤。If the instance expression is classified as a value, a binding-time error occurs. 由於 成員存取,相同的規則也適用于欄位。Because of Member access, the same rule also applies to fields.

假設宣告如下:Given the declarations:

struct Point
{
    int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int X {
        get { return x; }
        set { x = value; }
    }

    public int Y {
        get { return y; }
        set { y = value; }
    }
}

struct Rectangle
{
    Point a, b;

    public Rectangle(Point a, Point b) {
        this.a = a;
        this.b = b;
    }

    public Point A {
        get { return a; }
        set { a = value; }
    }

    public Point B {
        get { return b; }
        set { b = value; }
    }
}

在範例中in the example

Point p = new Point();
p.X = 100;
p.Y = 100;
Rectangle r = new Rectangle();
r.A = new Point(10, 10);
r.B = p;

p.X p.Y r.A r.B 由於 pr 都是變數,因此允許指派給、、和。the assignments to p.X, p.Y, r.A, and r.B are permitted because p and r are variables. 不過,在範例中However, in the example

Rectangle r = new Rectangle();
r.A.X = 10;
r.A.Y = 10;
r.B.X = 100;
r.B.Y = 100;

由於和不是變數,因此指派全都無效 r.A r.Bthe assignments are all invalid, since r.A and r.B are not variables.

複合指派Compound assignment

如果複合指派的左運算元是表單 E.PE[Ei] E 具有編譯時間類型的 dynamic ,則指派會動態繫結 (動態繫結) 。If the left operand of a compound assignment is of the form E.P or E[Ei] where E has the compile-time type dynamic, then the assignment is dynamically bound (Dynamic binding). 在此情況下,指派運算式的編譯時間類型是 dynamic ,而且下面所述的解析將會在執行時間根據的執行時間類型進行 EIn this case the compile-time type of the assignment expression is dynamic, and the resolution described below will take place at run-time based on the run-time type of E.

x op= y 由套用二元運算子多載解析 (二元運算子 多載解析) 和寫入作業的方式來處理表單的作業 x op yAn operation of the form x op= y is processed by applying binary operator overload resolution (Binary operator overload resolution) as if the operation was written x op y. 如此一來,Then,

  • 如果所選運算子的傳回型別可隱含轉換成的型別 x ,則作業會評估為 x = x op y ,但是 x 只會評估一次。If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.
  • 否則,如果選取的運算子是預先定義的運算子,則如果所選運算子的傳回型別明確地轉換成的型別 x ,而且如果 y 可以隱含地轉換成的型別 x 或運算子是移位運算子,則作業會評估為 x = (T)(x op y) ,其中 T 是的型別, x 但是 x 只會評估一次。Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of x, and if y is implicitly convertible to the type of x or the operator is a shift operator, then the operation is evaluated as x = (T)(x op y), where T is the type of x, except that x is evaluated only once.
  • 否則,複合指派是不正確,而且會發生系結階段錯誤。Otherwise, the compound assignment is invalid, and a binding-time error occurs.

「只評估一次」一詞表示在的評估中 x op y ,會暫時儲存任何組成運算式的結果, x 然後在執行指派給時重複使用 xThe term "evaluated only once" means that in the evaluation of x op y, the results of any constituent expressions of x are temporarily saved and then reused when performing the assignment to x. 例如,在指派中 A()[B()] += C() ,其中是傳回的 A 方法 int[] ,且 B 和都是傳回的 C 方法 int ,則只會叫用一次方法,順序為 A B CFor example, in the assignment A()[B()] += C(), where A is a method returning int[], and B and C are methods returning int, the methods are invoked only once, in the order A, B, C.

當複合指派的左運算元是屬性存取或索引子存取時,屬性或索引子必須同時具有存取子 get 和存取子 setWhen the left operand of a compound assignment is a property access or indexer access, the property or indexer must have both a get accessor and a set accessor. 如果不是這種情況,就會發生系結階段錯誤。If this is not the case, a binding-time error occurs.

上述的第二個規則允許 x op= y x = (T)(x op y) 在特定內容中進行評估。The second rule above permits x op= y to be evaluated as x = (T)(x op y) in certain contexts. 當左運算元的類型為、、、或時,規則就會存在,讓預先定義的運算子可以當做複合運算子使用 sbyte byte short ushort charThe rule exists such that the predefined operators can be used as compound operators when the left operand is of type sbyte, byte, short, ushort, or char. 即使兩個引數都是這些類型的其中之一,預先定義的運算子仍會產生類型的結果 int ,如 二進位數值升級中所述。Even when both arguments are of one of those types, the predefined operators produce a result of type int, as described in Binary numeric promotions. 因此,若沒有轉換,則無法將結果指派給左運算元。Thus, without a cast it would not be possible to assign the result to the left operand.

預先定義之運算子的規則直覺效果,就是 x op= y 如果 x op y 允許和都是允許的 x = yThe intuitive effect of the rule for predefined operators is simply that x op= y is permitted if both of x op y and x = y are permitted. 在範例中In the example

byte b = 0;
char ch = '\0';
int i = 0;

b += 1;             // Ok
b += 1000;          // Error, b = 1000 not permitted
b += i;             // Error, b = i not permitted
b += (byte)i;       // Ok

ch += 1;            // Error, ch = 1 not permitted
ch += (char)1;      // Ok

每個錯誤的直覺原因是對應的簡單指派也會發生錯誤。the intuitive reason for each error is that a corresponding simple assignment would also have been an error.

這也表示複合指派作業支援提升的作業。This also means that compound assignment operations support lifted operations. 在範例中In the example

int? i = 0;
i += 1;             // Ok

使用提升運算子 +(int?,int?)the lifted operator +(int?,int?) is used.

事件指派Event assignment

如果 or 運算子的左運算元 += -= 分類為事件存取,則運算式的評估方式如下:If the left operand of a += or -= operator is classified as an event access, then the expression is evaluated as follows:

  • 評估事件存取的實例運算式(如果有的話)。The instance expression, if any, of the event access is evaluated.
  • += -= 系統會評估 or 運算子的右運算元,並在必要時,透過隱含轉換 (隱含轉換) 來轉換成左運算元的類型。The right operand of the += or -= operator is evaluated, and, if required, converted to the type of the left operand through an implicit conversion (Implicit conversions).
  • 系統會叫用事件的事件存取子,並在評估之後、以及(如有必要)轉換之後,使用由右運算元組成的引數清單。An event accessor of the event is invoked, with argument list consisting of the right operand, after evaluation and, if necessary, conversion. 如果運算子是,則會叫用 += add 存取子; 如果運算子是 -= ,則 remove 會叫用存取子。If the operator was +=, the add accessor is invoked; if the operator was -=, the remove accessor is invoked.

事件指派運算式不會產生值。An event assignment expression does not yield a value. 因此,事件指派運算式只在 statement_expression (運算式語句) 的內容中有效。Thus, an event assignment expression is valid only in the context of a statement_expression (Expression statements).

運算式Expression

運算式 可以是 non_assignment_expression指派An expression is either a non_assignment_expression or an assignment.

expression
    : non_assignment_expression
    | assignment
    ;

non_assignment_expression
    : conditional_expression
    | lambda_expression
    | query_expression
    ;

常數運算式Constant expressions

Constant_expression 是可在編譯時期完整評估的運算式。A constant_expression is an expression that can be fully evaluated at compile-time.

constant_expression
    : expression
    ;

常數運算式必須是 null 常值或具有下列其中一種類型的值:、、、、、、、、、、、、、、 sbyte byte short ushort int uint long ulong char float double decimal bool object string 或任何列舉類型。A constant expression must be the null literal or a value with one of the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, object, string, or any enumeration type. 常數運算式中只允許下列結構:Only the following constructs are permitted in constant expressions:

  • 常值 (包括 null 常值) 。Literals (including the null literal).
  • const類別和結構類型成員的參考。References to const members of class and struct types.
  • 列舉類型成員的參考。References to members of enumeration types.
  • const參數或本機變數的參考References to const parameters or local variables
  • 括弧內的子運算式,也就是常數運算式。Parenthesized sub-expressions, which are themselves constant expressions.
  • Cast 運算式(如果目標型別是以上所列的其中一種類型的話)。Cast expressions, provided the target type is one of the types listed above.
  • checkedunchecked 運算式checked and unchecked expressions
  • 預設值運算式Default value expressions
  • Nameof 運算式Nameof expressions
  • 預先定義的 + 、、 - !~ 一元運算子。The predefined +, -, !, and ~ unary operators.
  • 預先定義的、、、、、、、、、、、、、、、、 + - * / % << >> & | ^ && || == != < > <=>= 二元運算子,提供的每個運算元都是上述類型。The predefined +, -, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and >= binary operators, provided each operand is of a type listed above.
  • ?:條件運算子。The ?: conditional operator.

常數運算式中允許下列轉換:The following conversions are permitted in constant expressions:

  • 身分識別轉換Identity conversions
  • 數值轉換Numeric conversions
  • 列舉轉換Enumeration conversions
  • 常數運算式轉換Constant expression conversions
  • 隱含和明確的參考轉換,前提是轉換的來源是評估為 null 值的常數運算式。Implicit and explicit reference conversions, provided that the source of the conversions is a constant expression that evaluates to the null value.

常數運算式中不允許其他轉換,包括非 null 值的裝箱、取消裝箱和隱含參考轉換。Other conversions including boxing, unboxing and implicit reference conversions of non-null values are not permitted in constant expressions. 例如:For example:

class C {
    const object i = 5;         // error: boxing conversion not permitted
    const object str = "hello"; // error: implicit reference conversion
}

因為需要進行裝箱轉換,所以我的初始化是錯誤。the initialization of i is an error because a boxing conversion is required. 因為需要非 null 值的隱含參考轉換,所以 str 的初始化是錯誤。The initialization of str is an error because an implicit reference conversion from a non-null value is required.

每次運算式滿足上述的需求時,就會在編譯時期評估運算式。Whenever an expression fulfills the requirements listed above, the expression is evaluated at compile-time. 即使運算式是包含非常數結構之較大運算式的子運算式,也是如此。This is true even if the expression is a sub-expression of a larger expression that contains non-constant constructs.

常數運算式的編譯時間評估會使用與非常數運算式之執行時間評估相同的規則,但在此情況下,執行時間評估會擲回例外狀況,編譯時間評估會導致編譯時期錯誤發生。The compile-time evaluation of constant expressions uses the same rules as run-time evaluation of non-constant expressions, except that where run-time evaluation would have thrown an exception, compile-time evaluation causes a compile-time error to occur.

除非在內容中明確放置常數運算式,否則在 unchecked 運算式的編譯時間評估期間,整數型別算數運算和轉換期間發生的溢位,一律會造成編譯時期錯誤 (常數運算式) 。Unless a constant expression is explicitly placed in an unchecked context, overflows that occur in integral-type arithmetic operations and conversions during the compile-time evaluation of the expression always cause compile-time errors (Constant expressions).

常數運算式會出現在下列內容中。Constant expressions occur in the contexts listed below. 在這些內容中,如果無法在編譯時期完整評估運算式,就會發生編譯時期錯誤。In these contexts, a compile-time error occurs if an expression cannot be fully evaluated at compile-time.

隱含常數運算式 轉換 (隱含常數運算式轉換) 允許將類型的常數運算式 int 轉換成 sbytebyteshortushortuintulong ,前提是常數運算式的值是在目的地類型的範圍內。An implicit constant expression conversion (Implicit constant expression conversions) permits a constant expression of type int to be converted to sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant expression is within the range of the destination type.

布林運算式Boolean expressions

Boolean_expression 是在特定內容中直接或透過應用程式產生型別結果的運算式, bool operator true 如下列所指定。A boolean_expression is an expression that yields a result of type bool; either directly or through application of operator true in certain contexts as specified in the following.

boolean_expression
    : expression
    ;

If_statement (if 語句的控制條件運算式) 、 while_statement (while 語句 ) 、do_statement ( do 語句) ,或 for_statement (for 語句) 是 boolean_expressionThe controlling conditional expression of an if_statement (The if statement), while_statement (The while statement), do_statement (The do statement), or for_statement (The for statement) is a boolean_expression. 運算子的控制條件運算式 ?: (條件運算子) 遵循與 boolean_expression 相同的規則,但基於運算子優先順序的原因,會分類為 conditional_or_expressionThe controlling conditional expression of the ?: operator (Conditional operator) follows the same rules as a boolean_expression, but for reasons of operator precedence is classified as a conditional_or_expression.

需要 boolean_expression 才能 E 產生型別的值,如下所示 boolA boolean_expression E is required to be able to produce a value of type bool, as follows:

  • 如果在 E bool 執行時間可隱含轉換為,則會套用隱含轉換。If E is implicitly convertible to bool then at runtime that implicit conversion is applied.
  • 否則,一元運算子多載解析 (一元運算子 多載解析) 用來在上尋找運算子的唯一最佳實作為 true E ,並在執行時間套用該實作為。Otherwise, unary operator overload resolution (Unary operator overload resolution) is used to find a unique best implementation of operator true on E, and that implementation is applied at runtime.
  • 如果找不到這類運算子,就會發生系結階段錯誤。If no such operator is found, a binding-time error occurs.

DBBool資料庫布林值型別中的結構類型提供實和的型別 operator true 範例 operator falseThe DBBool struct type in Database boolean type provides an example of a type that implements operator true and operator false.