表达式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成员访问)的左侧,或作为 @no__t 运算符(as 运算符)、is 运算符(is 运算符)或 @no 的操作数。__t-6 运算符(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.
  • 空文本。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. 调用实例属性访问的访问器(@no__t 0 或 @no__t 块)时,实例表达式的计算结果将成为 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. 如果调用了索引器访问的访问器(@no__t 0 或 @no__t 块),则实例表达式的计算结果将成为由 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. 当表达式调用的方法的返回类型为 void 时,会发生这种情况。This occurs when the expression is an invocation of a method with a return type of void. 归类为 nothing 的表达式仅在statement_expressionexpression 语句)的上下文中有效。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. 在这种情况下,如果实际表达式表示命名空间、类型、方法组或不执行任何操作,则会发生编译时错误。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
  • 重载的二元运算符: +-*/%&&&|||??,0,4,5,6,7,8Overloaded binary operators: +, -, *, /, %, &, &&, |, ||, ??, ^, <<, >>, ==,!=, >, <, >=, <=
  • 赋值运算符: @no__t 0,+=-=*=/=%=&=|=^=<<=,0Assignment 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)

前两个调用是静态绑定的:根据参数的编译时类型选取 @no__t 的重载。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.

第三个调用是动态绑定的:根据参数的运行时类型选取 @no__t 的重载。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) 中,将使用旧值 i 调用方法 F,然后使用旧值 i 调用方法 @no__t,最后,将使用新值 i 调用方法 @no__t。For 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),因为 * 运算符的优先级高于 binary + 运算符。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由由 @no__t 2 或 @no__t 3 运算符分隔的一系列multiplicative_expression组成,因此,+ 和 @no__t 5 运算符的优先级低于 @no__t 6、/% 运算符。For example, an additive_expression consists of a sequence of multiplicative_expressions 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 + - ! ~ ++x --x (T)x+ - ! ~ ++x --x (T)x
算术运算符Arithmetic operators 乘法Multiplicative * / %* / %
算术运算符Arithmetic operators 加法Additive + -+ -
移位运算符Shift operators 移位Shift << >><< >>
关系和类型测试运算符Relational and type-testing operators 关系和类型测试Relational and type testing < > <= >= is as< > <= >= is as
关系和类型测试运算符Relational and type-testing operators 相等Equality == !=== !=
逻辑运算符Logical operators 逻辑“与”Logical AND &
逻辑运算符Logical operators 逻辑 XORLogical XOR ^
逻辑运算符Logical operators 逻辑“或”Logical OR |
条件逻辑运算符Conditional logical operators 条件“与”Conditional AND &&
条件逻辑运算符Conditional logical operators 条件“或”Conditional 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 合并运算符和条件运算符(@no__t)是***右结合***运算符,这意味着运算从右到左进行。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 先计算 yz,并将结果与 x 相加,而 (x + y) * z 则先计算 xy,然后将结果与 z 相乘。For 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

尽管在表达式中不显式使用 @no__t 0 和 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. 具体而言,无法重载成员访问、方法调用或 =&&||???:=>checkeduncheckednewtypeof,0,1,2 运算符。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. 例如,运算符 @no__t 的重载也是运算符 *= 的重载。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. 例如,@no__t 0 运算符始终为二元运算符,始终具有在运算符优先级和关联性中指定的优先级别,并且始终为左结合。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. 例如,@no__t 的实现应比较两个操作数是否相等,并返回相应的 @no__t 结果。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 或 @no__t 为-1 的操作,其中 @no__t 为可重载的一元运算符,而 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:

  • @No__t 为操作 @no__t 提供的候选用户定义运算符集,使用候选用户定义的运算符的规则确定。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 是一个 X 类型的表达式,而第4个 @no__t 类型的表达式,则按如下方式进行处理: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:

  • 确定操作 operator op(x,y) @no__t 提供的候选用户定义运算符集,YThe 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. 如果 @no__t 为0,并且 @no__t 为同一类型,或者如果 X 和 @no__t 是从公共基类型派生的,则共享的候选运算符仅在组合集内出现一次。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. 否则,预定义的二进制 @no__t 0 实现(包括其提升形式)将成为操作的候选运算符集。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 所提供的候选用户定义运算符集: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. 如果 @no__t 是可以为 null 的类型,T0 是其基础类型,否则 T0 等于 TIf T is a nullable type, T0 is its underlying type, otherwise T0 is equal to T.
  • 对于 T0 和此类运算符的所有提升形式中的所有 operator op 声明,如果至少有一个运算符适用于参数列表 A,则候选运算符集包含所有此类@no__t 中的适用运算符。For 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.
  • 否则,如果 T0 @no__t 为-1,则候选运算符集为空。Otherwise, if T0 is object, the set of candidate operators is empty.
  • 否则,由 T0 提供的候选运算符集是 T0 的直接基类提供的候选运算符集,如果 T0 为类型参数,则为 T0 的有效基类。Otherwise, 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,其中 bbyte,而 s 是 @no__t @no__t,则重载决策会选择 "" 作为最佳运算符。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 是 @no__t 2,而 d 是 @no__t @no__t,则重载决策会将选择为最佳运算符。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

一元数值升级发生于预定义 +- 和 @no__t 一元运算符的操作数。Unary numeric promotion occurs for the operands of the predefined +, -, and ~ unary operators. 一元数值提升只包含将类型为 sbytebyteshortushortchar 的操作数转换为 int 类型。Unary numeric promotion simply consists of converting operands of type sbyte, byte, short, ushort, or char to type int. 此外,对于一元 - 运算符,一元数值提升将类型 @no__t 的操作数转换为类型 longAdditionally, for the unary - operator, unary numeric promotion converts operands of type uint to type long.

二进制数值升级Binary numeric promotions

二元数值升级发生在预定义的 +-*/%&、@no__t、@no__t、@no__t、@no__t、@no__t、@no__t、@no__t 和 @no__t 二进制运算符的操作数之间进行。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 或 @no__t,则会发生绑定时错误。If 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,另一个操作数将转换为 double 类型。Otherwise, if either operand is of type double, the other operand is converted to type double.
  • 否则,如果其中一个操作数的类型为 float,另一个操作数将转换为 float 类型。Otherwise, if either operand is of type float, the other operand is converted to type float.
  • 否则,如果其中一个操作数的类型为 ulong,另一个操作数将转换为类型 ulong,或者,如果另一个操作数的类型为 sbyteshortint 或 @no__t,则发生绑定时错误。Otherwise, 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,另一个操作数将转换为 long 类型。Otherwise, 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,另一个操作数将转换为 uint 类型。Otherwise, if either operand is of type uint, the other operand is converted to type uint.
  • 否则,两个操作数都将转换为类型 intOtherwise, both operands are converted to type int.

请注意,第一个规则不允许将 decimal 类型与 @no__t 和 @no__t 类型一起使用的任何操作。Note that the first rule disallows any operations that mix the decimal type with the double and float types. 规则如下所示,这是因为 decimal 类型与 @no__t 和 @no__t 2 类型之间没有隐式转换。The rule follows from the fact that there are no implicit conversions between the decimal type and the double and float types.

另请注意,如果一个操作数的类型为带符号整数类型,则操作数的类型不可能为 ulongAlso 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.

在上述两种情况下,强制转换表达式可用于将一个操作数显式转换为与另一个操作数兼容的类型。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);
}

发生绑定时错误,因为不能将 decimaldouble 进行相乘。a binding-time error occurs because a decimal cannot be multiplied by a double. 通过将第二个操作数显式转换为 decimal 来解决此错误,如下所示:The 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. 如果有一个或两个操作数为空(这是 bool? 类型的 @no__t 和 | 运算符(如布尔逻辑运算符中所述),则提升的运算符将产生 null 值。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 的值类型,并且结果类型为 bool,则该运算符的提升形式存在。a 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,则提升运算符将对操作数进行解包,并应用基础运算符来产生 @no__t 结果。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 的值类型,并且结果类型为 bool,则该运算符的提升形式存在。a 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,则提升运算符将生成值 falseThe lifted operator produces the value false if one or both operands are null. 否则,提升运算符将对操作数进行解包,并应用基础运算符来产生 @no__t 结果。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_expressionprimary_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.

对类型 @ no__t-3 中 K @ no__t-2type 参数的名称 @ no__t 的成员查找按如下方式进行处理:A member lookup of a name N with K type parameters in a type T is processed as follows:

  • 首先,确定一组名为 @ no__t 的可访问成员:First, a set of accessible members named N is determined:
    • 如果 @no__t 为类型参数,则该集是每个指定为 @ no__t-3 的主约束或辅助约束(类型参数约束)的每个类型中名为 @ no__t 的可访问成员集的并集,以及object 中名为 @ no__t 的可访问成员。If 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.
    • 否则,该集由 @ no__t-2 中名为 @ no__t 的所有可访问(成员访问)成员组成,其中包括继承的成员和 object 中名为 @ no__t-3 的可访问成员。Otherwise, the set consists of all accessible (Member access) members named N in T, including inherited members and the accessible members named N in object. 如果 @no__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.
  • 接下来,如果 @no__t 0 为零,则将删除所有声明都包含类型参数的嵌套类型。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. 请注意,当 @no__t 为零时,不会删除具有类型参数的方法,因为类型推理过程(类型推理)可以推断类型自变量。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 是声明成员 @ no__t-2 的类型,将应用以下规则: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:
    • 如果 @no__t 为常量、字段、属性、事件或枚举成员,则将从集合中删除在 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.
    • 如果 @no__t 为类型声明,则会从集中删除在 S 的基类型中声明的所有非类型,并且将从该集内删除与在 S 的基类型中声明的 @no__t 类型相同的所有类型声明。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.
    • 如果 @no__t 为方法,则将从集合中删除在 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. 此步骤仅在以下情况下有效: @no__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 的类型,如果 @no__t 为除 @no__t 之外的类声明,则应用以下规则: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 if S is a class declaration other than object:
    • 如果 @no__t 为常量、字段、属性、事件、枚举成员或类型声明,则会从集中删除在接口声明中声明的所有成员。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.
    • 如果 @no__t 为方法,则将从集合中删除在接口声明中声明的所有非方法成员,并且将从该集内删除与在接口声明中声明的 @no__t 相同的所有方法。If 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:

  • 如果 T @no__t 为-1,则 T 没有基类型。If T is object, then T has no base type.
  • 如果 @no__t 为,则 @no__t 的基类型为类型 System.EnumSystem.ValueType 和 @no__t。If T is an enum_type, the base types of T are the class types System.Enum, System.ValueType, and object.
  • 如果 @no__t 为struct_type,则 T 的基类型为类类型 System.ValueType 和 @no__t。If T is a struct_type, the base types of T are the class types System.ValueType and object.
  • 如果 @no__t 为class_type,则 @no__t 的基类型是 @no__t 的基类,包括类类型 objectIf T is a class_type, the base types of T are the base classes of T, including the class type object.
  • 如果 @no__t 为interface_type,则 @no__t 的基类型为-3 的基 @no__t 接口和类类型 objectIf T is an interface_type, the base types of T are the base interfaces of T and the class type object.
  • 如果 @no__t 为array_type,则 T 的基类型为类类型 System.Array 和 @no__t。If T is an array_type, the base types of T are the class types System.Array and object.
  • 如果 @no__t 为delegate_type,则 T 的基类型为类类型 System.Delegate 和 @no__t。If 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
  • EventsEvents
  • 索引器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. 在表中,exy,而 value 表示分类为变量或值的表达式,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) 应用重载决策,以选择包含类或结构中 @no__t 为0的最佳方法。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,则实例表达式 @no__t 为-1。If 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. 如果方法不 static,则会发生绑定时错误。A 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) 重载决策适用于在类、结构或接口中选择 e 类型给定的最佳方法 F。Overload resolution is applied to select the best method F in the class, struct, or interface given by the type of e. 如果方法 static,则会发生绑定时错误。A 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 调用包含类或结构中的属性 @no__t @no__t 的访问器。The get accessor of the property P in the containing class or struct is invoked. 如果 P 是只写的,则会发生编译时错误。A compile-time error occurs if P is write-only. 如果 Pstatic,则实例表达式为 thisIf P is not static, the instance expression is this.
P = value 在包含类或结构中,属性 P 的 @no__t 访问器是通过参数列表 (value) 来调用的。The set accessor of the property P in the containing class or struct is invoked with the argument list (value). 如果 @no__t 为只读,则会发生编译时错误。A compile-time error occurs if P is read-only. 如果 Pstatic,则实例表达式为 thisIf P is not static, the instance expression is this.
T.P 调用类或结构 T 中属性 @no__t @no__t 的访问器。The get accessor of the property P in the class or struct T is invoked. 如果 P 不 @no__t 为-1 或 P 为只写时,则会发生编译时错误。A compile-time error occurs if P is not static or if P is write-only.
T.P = value 类或结构中的属性 P 的 @no__t 访问器 T 是通过参数列表 (value) 调用的。The set accessor of the property P in the class or struct T is invoked with the argument list (value). 如果 P 不 @no__t 为-1 或 P 为只读,则会发生编译时错误。A compile-time error occurs if P is not static or if P is read-only.
e.P 在类、结构或接口 @no__t 类型给定的类、结构或接口中,@no__t 的属性的 P 的访问器由实例表达式 e 调用。The 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 @no__t 为-1 或 P 是只写的,则会发生绑定时错误。A binding-time error occurs if P is static or if P is write-only.
e.P = value 在类、结构或接口 @no__t 类型给定的类、结构或接口中,使用实例表达式 e 和参数列表 @no__t P 的属性 @no__t 访问器。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 @no__t 为-1 或 P 为只读,则会发生绑定时错误。A binding-time error occurs if P is static or if P is read-only.
事件访问Event access E += value 调用包含类或结构中的事件 @no__t 的 @no__t 取值函数。The add accessor of the event E in the containing class or struct is invoked. 如果 E 不是静态的,则实例表达式 @no__t 为-1。If E is not static, the instance expression is this.
E -= value 调用包含类或结构中的事件 @no__t 的 @no__t 取值函数。The remove accessor of the event E in the containing class or struct is invoked. 如果 E 不是静态的,则实例表达式 @no__t 为-1。If E is not static, the instance expression is this.
T.E += value 调用类或结构 T 中事件 @no__t 的 @no__t 0 访问器。The add accessor of the event E in the class or struct T is invoked. 如果 E 不是静态的,则会发生绑定时错误。A binding-time error occurs if E is not static.
T.E -= value 调用类或结构 T 中事件 @no__t 的 @no__t 0 访问器。The remove accessor of the event E in the class or struct T is invoked. 如果 E 不是静态的,则会发生绑定时错误。A binding-time error occurs if E is not static.
e.E += value 在类、结构或接口的 @no__t 类型指定的类、结构或接口中,事件 @no__t 的 E 的访问器由实例表达式 e 调用。The 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. 如果 E 为静态,则会发生绑定时错误。A binding-time error occurs if E is static.
e.E -= value 在类、结构或接口的 @no__t 类型指定的类、结构或接口中,事件 @no__t 的 E 的访问器由实例表达式 e 调用。The 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. 如果 E 为静态,则会发生绑定时错误。A 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 重载决策适用于在类、结构或接口中选择 e 类型给定的最佳索引器。Overload 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 应用重载决策,以选择类或结构中由 @no__t 类型给定的最佳一元运算符。Overload 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 重载决策适用于在类或结构中选择 xy 类型给定的最佳二进制运算符。Overload 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) 应用重载决策,以选择类或结构 T 中的最佳实例构造函数。Overload 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. 对于索引器,调用 @no__t 0 访问器时,参数列表还包括指定为赋值运算符的右操作数的表达式。For indexers, when invoking the set accessor, the argument list additionally includes the expression specified as the right operand of the assignment operator.
  • 对于属性,调用 get 访问器时,参数列表是空的,并且包含指定为赋值 @no__t 运算符的右操作数的表达式。For 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 和 output 参数。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 arguments, 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变量引用),指示参数将作为 Output 参数传递(输出参数)。The keyword out followed by a variable_reference (Variable references), indicating that the argument is passed as an output parameter (Output parameters). 在将变量作为 output 参数传递的函数成员调用之后,变量被视为明确赋值(明确赋值)。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.
    • 对于索引器,调用 @no__t 0 访问器时,指定为赋值运算符的右操作数的表达式对应于 @no__t 2 访问器声明的隐式 value 参数。For 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. 调用 @no__t 0 访问器时,指定为赋值运算符的右操作数的表达式对应于第 2 @no__t 访问器声明的隐式 value 参数。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.
  • 对于用户定义的一元运算符(包括转换),单个操作数对应于运算符声明的单个参数。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 或 output 参数的变量引用是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.ArrayTypeMismatchExceptionIf 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

数组协方差规则(数组协方差)允许数组类型 @no__t 值为对 B[] 的数组类型实例的引用,前提是存在从 BA 的隐式引用转换。The 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的数组元素作为 reference 或 output 参数传递时,需要运行时检查以确保数组的实际元素类型与参数的实际元素类型相同。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
    }
}

第二次调用 @no__t 将导致引发 System.ArrayTypeMismatchException,因为 b 的实际元素类型为 string 而不是 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>

通过类型推理,将 intstring 的类型参数从方法的自变量确定。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) 形式的方法调用时,类型推理的任务是查找唯一类型参数 @no__t 对于每个 @no__t 类型参数为-1,以便调用 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固定到特定类型 @no__t- 2 或未固定的一组边界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. 每个边界都是 T 类型。Each 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:

  • 如果 @no__t 为匿名函数,则显式参数类型推理显式参数类型推断)将从 @no__t 3 到 TiIf Ei is an anonymous function, an explicit parameter type inference (Explicit parameter type inferences) is made from Ei to Ti
  • 否则,如果 Ei 的类型 @no__t 为-1,xi 为值参数,则将UTi 进行下限推理Otherwise, if Ei has a type U and xi is a value parameter then a lower-bound inference is made from U to Ti.
  • 否则,如果 Ei 的类型 @no__t 为-1,xi 为 @no__t 或 @no__t 参数,则将@no__t 7@no__t 进行精确推断Otherwise, 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.
  • 否则,对于具有相应参数 @no__t 类型的所有参数 Ei,其中输出类型输出类型)包含未固定的类型变量 Xj输入类型(输入类型)不包含, 输出类型推理输出类型推断由 @no__t-* 11到*Ti3。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

如果 @no__t 为方法组或隐式类型的匿名函数,并且 T 为委托类型或表达式树类型,则 T 的所有参数类型均为类型T 的 @no__t 的输入类型If 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

如果 @no__t 为方法组或匿名函数,T 为委托类型或表达式树类型,则 @no__t 的返回类型为类型为 @no__t 的 E输出类型If 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直接依赖于未固定的类型 @no__t 变量。如果对于某个参数 Ek 类型为 Tk @no__t- 6, *则为类型-5 @no__t*类型为 3 的 @no__t 的输出类型。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 如果 @no__t*直接依赖于*`Xi`,或者 Xi直接依赖XkXk依赖于1。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 的委托类型或表达式树类型,则为下限推理下限推断)由 @no__t-* 8到*0。If 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.
  • 否则,如果 @no__t 为方法组,T 为具有参数类型的委托类型或表达式树类型 @no__t 为-2 并且返回类型为 Tb,且类型为 T1...Tk 的重载决策生成了返回类型为 U 的单个方法,则U1 进行下限推理Otherwise, 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.
  • 否则,如果 @no__t 为 U 类型的表达式,则将UT 进行下限推理Otherwise, 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类型 T 的表达式进行的:An explicit parameter type inference is made from an expression E to a type T in the following way:

  • 如果 E@no__t 为 @no__t 的参数类型U1...Uk为-1 的显式类型匿名函数,T 是具有参数类型 V1...Vk 的委托类型或表达式树类型,则对每个 Ui 进行精确推理(精确推断)从到对应的 0。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类型 V精确推理如下所示:An exact inference from a type U to a type V is made as follows:

  • 如果 V 是未固定的 @no__t 中的一个,则将 @no__t 添加到 Xi 的精确界限集。If V is one of the unfixed Xi then U is added to the set of exact bounds for Xi.

  • 否则,通过检查是否存在以下任何情况,确定了设置 V1...VkU1...UkOtherwise, sets V1...Vk and U1...Uk are determined by checking if any of the following cases apply:

    • @no__t 为数组类型,@no__t 为-1,U 为同一排名 @no__t 为3的数组类型V is an array type V1[...] and U is an array type U1[...] of the same rank
    • @no__t 类型 @no__t 为-1,U 为类型 U1?V is the type V1? and U is the type U1?
    • @no__t 为 @no__t 构造类型 U 是构造类型V is a constructed type C<V1...Vk>and U is a constructed type C<U1...Uk>

    如果上述任何情况都适用,则将每个 @no__t 2相应的 Vi 进行精确推理If 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@no__t 类型的下限推理如下所示:A lower-bound inference from a type U to a type V is made as follows:

  • 如果 V 是未固定的 @no__t 中的一个,则将 @no__t 添加到 Xi 的下限集。If V is one of the unfixed Xi then U is added to the set of lower bounds for Xi.

  • 否则,如果 @no__t 为 @no__t 类型 U 为 类型,则将从 @no__t 到 @no__t 进行下限推理。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...UkV1...VkOtherwise, sets U1...Uk and V1...Vk are determined by checking if any of the following cases apply:

    • @no__t 为数组类型,@no__t 为-1,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

    • @no__t 为 IEnumerable<V1>ICollection<V1>IList<V1>,@no__t 为一维数组类型 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[])

    • @no__t 为构造的类、结构、接口或委托类型,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>.

      ("唯一性" 限制意味着在 case 接口 C<T> {} class U: C<X>, C<Y> {} 中,当从 @no__t 推断到 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相应的 Vi 进行推断,如下所示:If 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
    • 否则,如果 @no__t 为数组类型,则进行下限推理Otherwise, if U is an array type then a lower-bound inference is made
    • 否则,如果 V @no__t 为-1,则推理依赖于第 i 个 @no__t 类型参数:Otherwise, 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 是未固定的 @no__t 中的一个,则将 @no__t 添加到 Xi 的上限。If V is one of the unfixed Xi then U is added to the set of upper bounds for Xi.

  • 否则,通过检查是否存在以下任何情况,确定了设置 V1...VkU1...UkOtherwise, sets V1...Vk and U1...Uk are determined by checking if any of the following cases apply:

    • @no__t 为数组类型,@no__t 为-1,V 为同一排名 @no__t 为3的数组类型U is an array type U1[...] and V is an array type V1[...] of the same rank

    • @no__t 为 IEnumerable<Ue>ICollection<Ue>IList<Ue>,@no__t 为一维数组类型 Ve[],则为0U is one of IEnumerable<Ue>, ICollection<Ue> or IList<Ue> and V is a one-dimensional array type Ve[]

    • @no__t 类型 @no__t 为-1,V 为类型 V1?U is the type U1? and V is the type V1?

    • @no__t 为构造的类、结构、接口或委托类型,@no__t 为-1,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>. 不是从 U1X<Q>Y<Q> 进行推断。)Inferences are not made from U1 to either X<Q> or Y<Q>.)

    如果满足上述任一情况,则会每个 Ui相应的 Vi 进行推断,如下所示:If 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
    • 否则,如果 @no__t 为数组类型,则进行上限推理Otherwise, if V is an array type then an upper-bound inference is made
    • 否则,如果 U @no__t 为-1,则推理依赖于第 i 个 @no__t 类型参数:Otherwise, 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 作为 @no__t 的边界集中的所有类型的集合开始。The set of candidate types Uj starts out as the set of all types in the set of bounds for Xi.
  • 然后,再次检查每个对 Xi 的约束:对于每个完全绑定 UXiUj 的所有类型与从候选集删除 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. 对于 @no__t 的每个下限 XiUj 的所有类型,会从候选集中删除从 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. 对于 Xi 的每个上限 所有类型 Uj,其中没有到 @no__t- 4 的隐式转换已从候选集删除。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.
  • 如果其余的候选类型 @no__t 为-0,则有一个唯一类型 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

在类型推理和重载决策过程中,将使用推断出的匿名函数的返回类型 FThe 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:

  • 如果 @no__t 的主体为具有类型的表达式,则 @no__t 的推断结果类型为该表达式的类型。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.
  • 如果 @no__t 的主体为,并且块的 return 语句中的表达式集具有最常见的类型 T查找一组表达式的最佳通用类型),则 F 的推理结果类型为 TIf 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.
  • 否则,将无法为 F 推断结果类型。Otherwise, a result type cannot be inferred for F.

按如下方式确定推断出的返回类型The inferred return type is determined as follows:

  • 如果 @no__t 为 async 并且 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
  • 如果 @no__t 为 async 并且具有推断结果类型 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>.
  • 如果 @no__t 为非 async 并且具有推断结果类型 T,则推断出的返回类型为 TIf F is non-async and has an inferred result type T, the inferred return type is T.
  • 否则,无法为 @no__t 推断返回类型。Otherwise a return type cannot be inferred for F.

作为涉及匿名函数的类型推理的示例,请考虑在 @no__t 类中声明的 Select 扩展方法:As 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 具有类型 string 的 @no__t 3 属性,则可以使用 @no__t 5 方法选择客户列表的名称: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 参数与 @no__t 参数相关,推理 @no__t @no__t 为-3。First, the customers argument is related to the source parameter, inferring T to be Customer. 然后,使用上面所述的匿名函数类型推理过程,@no__t 为 @no__t 指定类型,而表达式 c.Name 与 @no__t 参数的返回类型相关联,则推断 @no__t 为 @no__t。Then, 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" 与 @no__t 参数相关,推理 @no__t @no__t 为-3。proceeds as follows: First, the argument "1:15:30" is related to the value parameter, inferring X to be string. 然后,将第一个匿名函数的参数 s,将推断类型 @no__t 为-1,并且表达式 TimeSpan.Parse(s)f1 的返回类型相关,推断 @no__t 为 @no__t。Then, 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 指定了推断类型 @no__t 为-1,并且表达式 t.TotalSecondsf2 的返回类型相关,推断 @no__t 为 @no__t。Finally, 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 转换为给定委托 @no__t 类型(方法组转换)时,也必须应用类型推理。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)

并且方法组 @no__t 分配给委托类型 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>

与 @no__t 为兼容(委托声明)。becomes 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 被视为未固定的,并D 的每个参数类型Uj 的每个参数类型(M 的相应参数 @no__t 类型)进行下限推理Instead, 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)

具有 Ei 作为参数。with the Ei as arguments.

更准确地说,推理最初使用未固定的类型变量 XMore precisely, the inference starts out with an unfixed type variable X. 然后每个 @no__t 2X 进行输出类型推断Output 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:

  • @No__t-0 中的每个参数都对应于函数成员声明中的一个参数(如相应的参数中所述),并且没有参数对应的任何参数都是可选参数。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
    • 对于 @no__t 0 或 out 参数,参数的类型与对应参数的类型相同。for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter. 毕竟,@no__t 0 或 out 参数是传递的参数的别名。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
    • 对于 @no__t 0 或 out 参数,参数的类型与对应参数的类型相同。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 定义***为比 @no__t***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

  • 对于每个参数,从 ExQx 的隐式转换不优于从 ExPx 的隐式转换和for each argument, the implicit conversion from Ex to Qx is not better than the implicit conversion from Ex to Px, and
  • 对于至少一个参数,从 ExPx 的转换优于从 ExQx 的转换。for at least one argument, the conversion from Ex to Px is better than the conversion from Ex to Qx.

当执行此计算时,如果 Mp 或 @no__t 为其展开形式适用,则 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.

如果参数类型序列 @ no__t 与 @no__t 是等效的(即每个 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.

  • 如果 @no__t 为非泛型方法,Mq 是泛型方法,则 MpMq 更好。If Mp is a non-generic method and Mq is a generic method, then Mp is better than Mq.
  • 否则,如果 Mp 适用于其正常窗体,Mq 具有 @no__t 2 数组,并且仅适用于其展开形式,则 MpMq 好。Otherwise, 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 比 @no__t 3 更好。Otherwise, if Mp has more declared parameters than Mq, then Mp is better than Mq. 如果两种方法都有 @no__t 0 数组,并且仅适用于其展开的形式,则会发生这种情况。This can occur if both methods have params arrays and are applicable only in their expanded forms.
  • 否则,如果 Mp 的所有参数都有相应的参数,而默认参数需要替换为 Mq 中的至少一个可选参数,则 MpMq 更好。Otherwise 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.
  • 否则,如果 @no__t 0 的参数类型比 Mq 更具体,则 MpMq 更好。Otherwise, if Mp has more specific parameter types than Mq, then Mp is better than Mq. {R1, R2, ..., Rn}{S1, S2, ..., Sn} 表示未实例化和未扩展的参数类型 Mp 和 @no__t。Let {R1, R2, ..., Rn} and {S1, S2, ..., Sn} represent the uninstantiated and unexpanded parameter types of Mp and Mq. 如果对于每个参数,@no__t 0 的参数类型比 @no__t 的参数类型更具体,但对于每个参数,Rx 的不太具体于 Sx,并且对于至少一个参数,Rx 比 @no__t 5 更具体: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,并将从表达式 @no__t- 4 转换为类型的隐式 @no__t 转换为 T2,@no__tt-sql 并不完全匹配 0 和至少一个以下保留项: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 和类型 T,如果下列其中一项保留,E 完全匹配 TGiven an expression E and a type T, E exactly matches T if one of the following holds:

  • @no__t 的类型 @no__t 为-1,并且存在从 ST 的标识转换E has a type S, and an identity conversion exists from S to T
  • @no__t 为匿名函数,T 既可以是 @no__t 的委托类型,也可以是一个 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:
    • D推断返回类型)的参数列表的上下文中存在 @no__t @no__t 为-1 的推理返回类型,并且存在从 X 到 @no__t 返回类型的标识转换An 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
    • @No__t 为非 async,D 具有 @no__t 为-2 的返回类型,或者 E 为 async,D 的返回类型为5,其中一种是以下保留: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:
      • @No__t 的正文是一个与 @no__t 完全匹配的表达式The body of E is an expression that exactly matches Y
      • @No__t 的主体是一个语句块,其中每个 return 语句返回一个与 @no__t 完全匹配的表达式The body of E is a statement block where every return statement returns an expression that exactly matches Y

更好的转换目标Better conversion target

给定两个不同的类型 T1T2,如果不存在从 @no__t 到 T1 的隐式转换,则 T1 是比 T2 更好的转换目标: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 的隐式转换An implicit conversion from T1 to T2 exists
  • @no__t 为委托类型 @no__t 为-1 或表达式目录树类型 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
    • @no__t 的返回类型 @no__t 为-1,S1 是比 S2 更好的转换目标D2 has a return type S2, and S1 is a better conversion target than S2
  • T1 @no__t 为-1,T2Task<S2>S1 是比 @no__t 更好的转换目标T1 is Task<S1>, T2 is Task<S2>, and S1 is a better conversion target than S2
  • @no__t 为 S1S1?,其中 S1 为有符号整数类型,T2S2S2?,其中 @no__t 为无符号整数类型。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:
    • S1 @no__t 为-1,S2byteushortuint,或者 ulongS1 is sbyte and S2 is byte, ushort, uint, or ulong
    • S1 @no__t 为-1,S2ushortuint 或 @no__t 5S1 is short and S2 is ushort, uint, or ulong
    • S1 @no__t 为-1,S2uint 或 @no__tS1 is int and S2 is uint, or ulong
    • S1 @no__t 为-1,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:

  • 如果 @no__t,则为静态函数成员: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:

    • 计算 EE is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, then no further steps are executed.
    • 如果 E 未分类为变量,则创建 @no__t 的类型的临时局部变量,并将 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. 临时变量可作为 thisM 内访问,但不能以其他方式访问。The temporary variable is accessible as this within M, but not in any other way. 因此,只有在 E 为真正的变量时,调用方才能观察 M 对 @no__t 的更改。Thus, 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. @No__t 引用的变量成为 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:

    • 计算 EE is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, then no further steps are executed.
    • 参数列表按参数列表中所述进行计算。The argument list is evaluated as described in Argument lists.
    • 如果 @no__t 的类型为value_type,则将执行装箱转换(装箱转换),将 @no__t 转换为类型 object,并在以下步骤中将 E 视为类型 @no__t。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 只能是 @no__t 的成员。In this case, M could only be a member of System.Object.
    • 检查 E 的值是否有效。The value of E is checked to be valid. 如果 E 的值 @no__t 为-1,则会引发 @no__t,而不执行进一步的步骤。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:
      • 如果 @no__t 的绑定时类型是接口,则调用的函数成员是由 E 引用的实例的运行时类型提供的 M 实现。If 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. 通过应用接口映射规则(接口映射)来确定此函数成员,以确定 E 引用的实例的运行时类型提供的 @no__t 的实现。This 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.
      • 否则,如果 @no__t 为虚拟函数成员,则调用的函数成员是由 E 引用的实例的运行时类型提供的 M 实现。Otherwise, 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. 此函数成员是通过应用规则来确定的,这些规则用于确定 E 引用的实例的运行时类型 M 的派生程度最高的实现(虚拟方法)。This 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.
      • 否则,@no__t 为非虚拟函数成员,调用的函数成员将 M 本身。Otherwise, 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. @No__t 引用的对象将成为 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:

  • 当函数成员是从类型 @no__t 继承的方法的 @no__t 0 时,将通过类型 object 的实例表达式调用。When 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的变量,此变量将成为函数成员调用中 this 所引用的变量。In 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_expressions 和primary_no_array_creation_expression之间。Primary expressions are divided between array_creation_expressions and primary_no_array_creation_expressions. 以这种方式对待数组创建表达式,而不是将其与其他简单的表达式窗体一起列出,而是允许语法禁止可能的代码混乱,如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必须具有到 int 的隐式转换。The 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.IFormattable,或者 System.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.Create 的调用。If 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.Format 的调用。If 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跟在 $ 符号后,则格式字符串文本就是该标记。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
    • 然后,对于每个数字 I0N-1Then for each number I from 0 to N-1:
      • @No__t 的十进制表示形式The 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_midinterpolated_verbatim_string_end紧靠在相应的内插之后。Then 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>,其中 @no__t 为单个标识符,<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:

  • 如果 @no__t 为零,且simple_name中出现,并且的(或封闭的)局部变量声明空间(声明)包含局部变量、参数或常量name @ no__t,则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.

  • 如果 @no__t 为零,且simple_name显示在泛型方法声明的主体中,并且如果声明包含名称为 @ no__t 的类型参数,则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.

  • 否则,对于每个实例类型 @ no__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):

    • 如果 @no__t 为零,而 @no__t 的声明包含名为 @ no__t 的 type 参数,则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.
    • 否则,如果 TI 的成员查找(成员查找)与 K @ no__t-4type 参数生成匹配项:Otherwise, if a member lookup (Member lookup) of I in T with K type arguments produces a match:
      • 如果 @no__t 为直接封闭类或结构类型的实例类型,并且查找标识了一个或多个方法,则结果为一个方法组,其实例表达式的 @no__t 为-1。If 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).
      • 否则,如果 @no__t 为直接封闭类或结构类型的实例类型,则如果查找标识实例成员,并且引用出现在实例构造函数、实例方法或实例访问器的主体中,则结果与 this.I 形式的成员访问(成员访问)相同。Otherwise, 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. 仅当 @no__t 0 为零时,才会发生这种情况。This can only happen when K is zero.
      • 否则,结果与 T.IT.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.
  • 否则,对于每个命名空间 @ no__t 0,从发生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:

    • 如果 @no__t 为零且 @no__t 为 @ no__t 中的命名空间的名称,则:If K is zero and I is the name of a namespace in N, then:
      • 如果发生simple_name的位置由 N 的命名空间声明括起来,并且命名空间声明包含将名称 @ no__t 与 a 关联的extern_alias_directiveusing_alias_directive ,命名空间或类型,则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引用 N 中名为 I 的命名空间。Otherwise, the simple_name refers to the namespace named I in N.
    • 否则,如果 N 包含名为 @ no__t 的可访问类型和 K @ no__t-3type 参数,则:Otherwise, if N contains an accessible type having name I and K type parameters, then:
      • 如果 @no__t 为零,且simple_name的位置由 N 的命名空间声明括起来,并且命名空间声明包含将name @ no__t-5,其中包含一个命名空间或类型,则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发生的位置由 @ no__t 的命名空间声明括起来,则为; 否则为。Otherwise, if the location where the simple_name occurs is enclosed by a namespace declaration for N:
      • 如果 @no__t 为零且命名空间声明包含extern_alias_directiveusing_alias_directive ,并将名称 @ no__t 与导入的命名空间或类型相关联,则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_directives 和using_static_directives 导入的命名空间和类型声明包含一个具有 name @ no__ 的可访问类型或非扩展静态成员,simple_name 和 K @ no__t-4type 参数,则引用以给定类型参数构造的类型或成员。Otherwise, if the namespaces and type declarations imported by the using_namespace_directives and using_static_directives 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所导入的命名空间和类型包含多个名为 @ no__t 和 K @ no__t-3type 参数的可访问类型或非扩展方法静态成员,simple_name是不明确的,并发生错误。Otherwise, if the namespaces and types imported by the using_namespace_directives 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_expressionA 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_access包含primary_expressionpredefined_typequalified_alias_member,后跟 "." 标记,后跟标识符,可选择后跟type_argument_list.A 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>,其中 @no__t 为主表达式,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.

类型为 dynamicprimary_expressionmember_access是动态绑定的(动态绑定)。A member_access with a primary_expression of type dynamic is dynamically bound (Dynamic binding). 在这种情况下,编译器会将成员访问分类为类型 @no__t 的属性访问。In 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:

  • 如果 @no__t 0 为零且 @no__t 为命名空间,并且 E 包含名为 @ no__t 的嵌套命名空间,则结果为该命名空间。If K is zero and E is a namespace and E contains a nested namespace with name I, then the result is that namespace.
  • 否则,如果 @no__t 为命名空间,E 包含名为 @ no__t 的可访问类型和 @no__t @ 4type 参数,则结果是使用给定的类型参数构造的类型。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.
  • 如果 E 是分类为类型的predefined_typeprimary_expression ,则如果 E 不是类型参数,并且 @no__t-@no__t 6 中的 I 的成员查找(成员查找)生成匹配项,然后按如下所示对 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 标识 @no__t 属性,则结果为无关联实例表达式的属性访问。If I identifies a static property, then the result is a property access with no associated instance expression.
    • 如果 I 标识 @no__t 字段:If I identifies a static field:
      • 如果字段为 readonly,并且引用出现在声明该字段的类或结构的静态构造函数之外,则结果为值,即 @ no__t 中静态字段 @ no__t 的值。If 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.
      • 否则,结果为变量,即 @ no__t 中的静态字段 @ no__t-0。Otherwise, the result is a variable, namely the static field I in E.
    • 如果 @no__t,则标识 @no__t 1 事件:If I identifies a static event:
      • 如果引用出现在声明事件的类或结构中,并且声明事件时没有event_accessor_declarations事件),则会完全按 I 为静态字段的方式处理 @no__t。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.
    • 否则,@no__t 为无效的成员引用,并发生编译时错误。Otherwise, E.I is an invalid member reference, and a compile-time error occurs.
  • 如果 @no__t 为 "属性访问"、"索引器访问"、"变量" 或 "值",其中的 "类型" 为 @ no__t,而 "no__t" @no__t 中的成员查找(成员查找@no__t)为 K @ 6type 参数生成匹配项,则对 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:
    • 首先,如果 @no__t 为属性或索引器访问,则获取属性或索引器访问的值(表达式的值),将 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,
      • 如果 @no__t 为 thisI 标识自动实现的属性(自动实现的属性),而不使用 setter,并且引用出现在类或结构类型为 T 的实例构造函数中,则结果是一个变量,即由 this 给定的 T 实例中 I 给定的自动属性的隐藏支持字段。If 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.
      • 否则,结果为具有 @ no__t 的关联实例表达式的属性访问。Otherwise, the result is a property access with an associated instance expression of E.
    • 如果 @no__t 为class_type ,则 I 标识该class_type的实例字段:If T is a class_type and I identifies an instance field of that class_type:
      • 如果 E 的值 @no__t 为-1,则会引发 System.NullReferenceExceptionIf the value of E is null, then a System.NullReferenceException is thrown.
      • 否则,如果字段 readonly,并且引用出现在声明该字段的类的实例构造函数之外,则结果为值,即 @ no__t-2 引用的对象中的字段 @ no__t 的值。Otherwise, 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.
      • 否则,结果为变量,即 @ no__t 引用的对象中的字段 @ no__t-0。Otherwise, the result is a variable, namely the field I in the object referenced by E.
    • 如果 @no__t 为struct_type ,则 I 标识该struct_type的实例字段:If T is a struct_type and I identifies an instance field of that struct_type:
      • 如果 @no__t 为值,或者如果该字段 @no__t 为-1,并且引用出现在声明该字段的结构的实例构造函数的外部,则结果为值,即 @ no__t 指定的结构实例中字段 @ no__t 的值。If 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.
      • 否则,结果为变量,即 @ no__t 指定的结构实例中的字段 @ no__t-0。Otherwise, 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事件)的情况下声明的,并且该引用不是作为 @no__t 或 -= 运算符的左侧出现,然后处理 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.
      • 否则,结果为具有 @ no__t 的关联实例表达式的事件访问。Otherwise, 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 的成员访问中,如果 @no__t 为单个标识符,并且 E 的含义为simple_name简单名称)为常量、字段、属性、局部变量或参数,其类型与 E 的含义相同。type_name命名空间和类型名称),则允许 E 的可能含义。In 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. @No__t-0 的两种可能含义永远都不明确,因为 @no__t 在这两种情况下,-1 必须是该类型 @no__t 的成员。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));

可以解释为对 @no__t 的调用-0,其中包含两个参数,G < AB > (7)could be interpreted as a call to F with two arguments, G < A and B > (7). 或者,它可能被解释为对 F 的调用,其中一个参数是对具有两个类型参数和一个正则参数的泛型方法 @ no__t 的调用。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.

如果可以将标记序列(在上下文中)作为simple_name简单名称)、 member_access成员访问)或pointer_member_access指针成员访问)(以 type_argument_ 结尾)进行分析列表类型参数),将检查紧跟在结束 @no__t 标记后面的标记。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);

每个都将被解释为对具有两个自变量的 @no__t 的调用。will each be interpreted as a call to F with two arguments. 语句The statement

x = F < A > +y;

将解释为小于运算符、大于运算符和一元正运算符,就如同语句已写入 x = (F < A) > (+y),而不是 type_argument_list后跟二元加法运算符。will be interpreted as a less than operator, greater than operator, and unary plus operator, as if the statement had been written x = (F < A) > (+y), instead of as a simple_name with a type_argument_list followed by a binary plus operator. 在语句中In the statement

x = y is C<T> + z;

标记 C<T> 将解释为type_argument_listnamespace_or_type_namethe tokens C<T> are interpreted as a namespace_or_type_name with a type_argument_list.

调用表达式Invocation expressions

Invocation_expression用于调用方法。An 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分类为类型 @no__t 的值。In this case the compiler classifies the invocation_expression as a value of type dynamic. 下面的规则用于确定invocation_expression的含义,并使用运行时类型,而不是primary_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. 仅在statement_expressionexpression 语句)的上下文中或作为lambda_expression匿名函数表达式)的主体时,才允许将表达式归类为 nothing。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.

@No__t-0 形式的方法调用的绑定时处理,其中 M 是一个方法组(可能包括type_argument_list),而 @no__t 为可选的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:
    • 如果 @no__t 为非泛型,则在以下情况下 F 是候选项:If F is non-generic, F is a candidate when:
    • 如果 @no__t 为泛型且 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).
    • 如果 @no__t 为泛型且 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. 此外,如果 @no__t 为 object 以外的类类型,则将从集合中删除在接口类型中声明的所有方法。Furthermore, if C is a class type other than object, all methods declared in an interface type are removed from the set. (仅当方法组是具有非 object 和非空有效接口集的有效基类的类型形参上的成员查找结果时,这一规则才会生效。)(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_namemember_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. 如果任何类型自变量不满足类型参数上的相应约束,则会发生绑定时错误。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:

  • @no__t 为非泛型非泛型类,则为0Ci is a non-generic, non-nested class
  • @No__t 的名称为标识符The name of Mj is identifier
  • 当作为如下所示的静态方法应用于参数时,Mj 可访问且适用Mj is accessible and applicable when applied to the arguments as a static method as shown above
  • 存在从exprMj 的第一个参数类型的隐式标识、引用或装箱转换。An implicit identity, reference or boxing conversion exists from expr to the type of the first parameter of Mj.

@No__t 的搜索将按如下方式继续:The 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导入并直接在给定命名空间或编译 @no__t 单元中通过using_namespace_directive导入的命名空间中声明的类型 @no__t这些扩展方法集是候选集。If types Ci imported by using_static_declarations and directly declared in namespaces imported by using_namespace_directives 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.

使用 @no__t 0 作为目标,然后将方法调用处理为静态方法调用(对动态重载决策进行编译时检查)。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)
    }
}

在此示例中,@no__t 的方法优先于第一个扩展方法,@no__t 的方法优先于这两个扩展方法。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.GE.F 优先于 @no__t 3 和 @no__t。D.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_ 有关 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.

@No__t-0 形式的委托调用的运行时处理,其中 Ddelegate_typeprimary_expressionA 是一个可选的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:

  • 计算 DD is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, no further steps are executed.
  • 检查 D 的值是否有效。The value of D is checked to be valid. 如果 D 的值 @no__t 为-1,则会引发 @no__t,而不执行进一步的步骤。If the value of D is null, a System.NullReferenceException is thrown and no further steps are executed.
  • 否则,@no__t 为对委托实例的引用。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_access包含一个primary_no_array_creation_expression,后跟一个 "[" 标记,后跟一个argument_list,后跟一个 "]" 标记。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 arguments, separated by commas.

element_access
    : primary_no_array_creation_expression '[' expression_list ']'
    ;

不允许element_accessargument_list包含 ref 或 @no__t 3 参数。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至少有一个表达式的编译时 @no__t 类型为-1,并且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分类为类型 @no__t 的值。In this case the compiler classifies the element_access as a value of type dynamic. 下面的规则用于确定element_access的含义,并使用运行时类型,而不是primary_no_array_creation_expressionargument_list的编译时类型。编译时类型 @no__t 为的表达式。The 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的排名相同,并且每个表达式的类型必须为 intuintlongulong,或者必须能够隐式转换为这些类型中的一个或多个。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中的表达式的值所选择的数组元素的值。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.

@No__t-0 形式的数组访问的运行时处理,其中 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:

  • 计算 PP 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 到 @no__t,从 @no__t 到 @no__t 的隐式转换是可能的。For 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. 如果 P 的值 @no__t 为-1,则会引发 @no__t,而不执行进一步的步骤。If the value of P is null, a System.NullReferenceException is thrown and no further steps are executed.
  • Argument_list中每个表达式的值是根据 P 所引用的数组实例的每个维度的实际界限检查的。The 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.
  • 计算索引表达式给定的数组元素的位置,此位置成为数组访问的结果。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_listFor 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.

@No__t-0 格式的索引器访问的绑定时处理,其中 P 是类、结构或接口类型的primary_no_array_creation_expression T,而 A 是一个argument_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:

  • 构造 T 提供的一组索引器。The set of indexers provided by T is constructed. 该集包含在 T 中声明的所有索引器或不 override 声明且可在当前上下文(成员访问)中访问的 T 基类型。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 是声明索引器 I 的类型:The 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 对 @no__t (适用的函数成员)不适用,则从集中删除 @no__t。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适用的函数成员),而 @no__t 为除 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 的关联的实例表达式和 A 的关联自变量列表。The 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由保留字 this 组成。A 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:

  • 当在类的实例构造函数中的primary_expression中使用 this 时,它将分类为值。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.
  • 当在类的实例方法或实例访问器中的primary_expression中使用 this 时,它将分类为值。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.
  • 如果在结构的实例构造函数中的primary_expression中使用 this,则会将其归类为变量。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.
  • 如果在结构的实例方法或实例访问器中的primary_expression中使用 this,则会将其归类为变量。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 变量表示为其调用方法或访问器的结构,其行为与结构类型的 @no__t 2 参数完全相同。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.

如果在上面列出的上下文之外的primary_expression中使用了 this,则会出现编译时错误。Use of this in a primary_expression in a context other than the ones listed above is a compile-time error. 特别是,不能在静态方法、静态属性访问器或字段声明的variable_initializer中引用 thisIn 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.Ibase[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). 因此,在 @no__t 1 函数成员的 @no__t 中, 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.

如果后缀递增或递减运算的操作数是属性或索引器访问,则属性或索引器必须同时具有 @no__t 0 和 @no__t 取值函数。If 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. 预定义 ++-- 运算符对于以下类型存在: sbytebyteshortushortintuintlongulong、0、1、2、3 和任意枚举类型。Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. 预定义的 @no__t 0 运算符返回通过向操作数添加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.OverflowExceptionIn 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.

@No__t-0 或 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.
    • 保存 @no__t 值-0。The value of x is saved.
    • 将调用选定的运算符,并将 x 的值作为其参数。The selected operator is invoked with the saved value of x as its argument.
    • 运算符返回的值存储在 @no__t 的计算所得的位置。The value returned by the operator is stored in the location given by the evaluation of x.
    • @No__t 的保存值为运算结果。The saved value of x becomes the result of the operation.
  • 如果 x 归类为属性或索引器访问:If x is classified as a property or indexer access:
    • 实例表达式(如果 @no__t 0 不 static)和参数列表(如果 x 是索引器访问)与 x 相关联,并在后续的 @no__t 4 和 @no__t 访问器调用中使用结果。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.
    • 调用 @no__t 的 @no__t 取值函数-1,并保存返回值。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.
    • 使用由运算符返回的值作为其 value 参数调用 x 的 @no__t 取值函数。The set accessor of x is invoked with the value returned by the operator as its value argument.
    • @No__t 的保存值为运算结果。The saved value of x becomes the result of the operation.

@No__t 0 和 @no__t 1 运算符还支持前缀表示法(前缀增量和减量运算符)。The ++ and -- operators also support prefix notation (Prefix increment and decrement operators). 通常情况下,@no__t @no__t 的结果是在操作之前 x 的值,而 ++x--x 的结果是操作后 x 的值。Typically, 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.

可以使用后缀或前缀表示法来调用 @no__t 0 或 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

@No__t-0 运算符用于创建类型的新实例。The new operator is used to create new instances of types.

有三种形式的 @no__t 0 表达式: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.

@No__t-0 运算符表示创建类型的实例,但不一定表示动态分配内存。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),其中 T 是一个class_typevalue_typeA 是一个可选的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:

  • 如果 @no__t 为value_type ,则不存在 AIf 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 类型的值,也就是在 system.string类型中定义的 T 的默认值。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.
  • 否则,如果 @no__t 为type_parameter ,则不存在 AOtherwise, 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:
    • 如果 @no__t 为 abstract 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是动态绑定的,编译时类型仍 @no__t 为-1。Even if the object_creation_expression is dynamically bound, the compile-time type is still T.

Object_creation_expression的运行时处理形式 new T(A),其中 Tclass_typestruct_typeA 是一个可选的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:

  • 如果 @no__t,则为*class_type*:If T is a class_type:
    • 分配 T 类的新实例。A 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.
  • 如果 @no__t,则为*struct_type*:If 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
    ;

对象初始值设定项由成员初始值设定项的序列组成,括在 @no__t 0 和 } 标记之间,并用逗号分隔。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.

如果 @no__t 0 的构造函数分配两个嵌入的 @no__t 实例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; } }
}

以下构造可用于初始化嵌入的 @no__t 0 实例,而不是分配新实例: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)*
    ;

集合初始值设定项由元素初始值设定项的序列组成,其中由 { 和 @no__t 的标记括起来,并用逗号分隔。A collection initializer consists of a sequence of element initializers, enclosed by { and } tokens and separated by commas. 每个元素初始值设定项指定一个元素,该元素将被添加到要初始化的集合对象,并由一个由 { 和 @no__t 个标记括起来并用逗号分隔的表达式组成的列表组成。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. 因此,集合对象必须具有适用于每个元素初始值设定项的名称为 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] 将生成类型为 @no__t 的数组实例,而数组创建表达式 new int[10][,] 则生成 @no__t 类型为3的数组。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[][,]. 表达式列表中的每个表达式的类型必须为 intuintlongulong,或可隐式转换为这些类型中的一个或多个。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.

除非在不安全的上下文中(不安全上下文),否则不指定数组的布局。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中找到的所有表达式For a multidimensional array, i.e., one where the rank_specifier contains at least one comma, this set comprises all expressions found in nested array_initializers.

数组初始值设定项中进一步介绍了数组初始值设定项。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 和 @no__t 均不能隐式转换为另一种类型,因此没有最常见的类型。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),其中 D 是一个delegate_typeE 是一个表达式,它包含以下步骤: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:

  • 如果 @no__t 为方法组,则委托创建表达式的处理方式与从 E 到 @no__t 的方法组转换相同If 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 到 @no__t 的匿名函数转换相同的方式处理委托创建表达式。If 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 必须与 @no__t 兼容(委托声明),并且结果是对类型 D 的新创建的委托的引用,该委托引用与 @no__t 5 相同的调用列表。If 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 与 @no__t 不兼容,则会发生编译时错误。If E is not compatible with D, a compile-time error occurs.

Delegate_creation_expression 的的运行时处理形式 new D(E),其中 D 是一个delegate_typeE 是一个表达式,它包含以下步骤: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:

  • 如果 @no__t 为方法组,则委托创建表达式将作为从 E 到 @no__t 的方法组转换(方法组转换)进行评估。If E is a method group, the delegate creation expression is evaluated as a method group conversion (Method group conversions) from E to D.
  • 如果 @no__t 为匿名函数,则委托创建将作为从 ED匿名函数转换)的匿名函数转换进行计算。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:
    • 计算 EE is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, no further steps are executed.
    • 如果 E 的值 @no__t 为-1,则会引发 @no__t,而不执行进一步的步骤。If the value of E is null, a System.NullReferenceException is thrown and no further steps are executed.
    • 分配委托类型 @no__t 的新实例。A 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.
    • 使用与 E 给定的委托实例相同的调用列表来初始化新的委托实例。The 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 字段通过委托引用第二个 @no__t 方法,因为该方法与 DoubleFunc 的形参列表和返回类型完全匹配。the 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. 匿名类型是直接从 @no__t 继承的无类型类类型。An 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() { ... }
}

其中每个 @no__t 为 ex 的相应表达式的类型。where 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.

匿名类型上的 @no__t 0 和 @no__t 方法会重写从 object 继承的方法,并且是根据属性的 EqualsGetHashcode 定义的,因此,当且仅当其所有属性都为时,相同匿名类型的两个实例才相等。=.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 条件成员访问(空条件表达式作为投影初始值设定项)。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

@No__t-0 运算符用于获取类型的 @no__t 1 对象。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. 此形式的表达式的结果是指定类型的 @no__t 0 对象。The result of an expression of this form is the System.Type object for the indicated type. 任何给定类型都只有一个 @no__t 0 对象。There is only one System.Type object for any given type. 这意味着,对于类型 @ no__t-0,typeof(T) == typeof(T) 始终为 true。This means that for a type T, typeof(T) == typeof(T) is always true. 类型不能 @no__t 为-1。The type cannot be dynamic.

第二种形式的typeof_expression包含一个 typeof 关键字,后跟一个带圆括号的unbound_type_nameThe 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_lists。An unbound_type_name is very similar to a type_name (Namespace and type names) except that an unbound_type_name contains generic_dimension_specifiers where a type_name contains type_argument_lists. typeof_expression的操作数为满足unbound_type_nametype_name语法的标记序列时,即它既不包含generic_dimension_specifier也不包含type_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:

  • 将标记序列替换为type_name ,方法是将每个generic_dimension_specifier替换为具有相同数量的逗号的type_argument_list ,并将关键字 object 作为每个type_argumentConvert 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 关键字,后跟一个带圆括号的 void 关键字。The third form of typeof_expression consists of a typeof keyword followed by a parenthesized void keyword. 此形式的表达式的结果是 @no__t 0 对象,表示缺少类型。The result of an expression of this form is the System.Type object that represents the absence of a type. @No__t 返回的类型对象与为任何类型返回的类型对象不同。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.

可在类型参数上使用 @no__t 0 运算符。The typeof operator can be used on a type parameter. 结果是绑定到类型参数的运行时类型的 @no__t 0 对象。The result is the System.Type object for the run-time type that was bound to the type parameter. @No__t-0 运算符还可用于构造类型或未绑定的泛型类型(绑定类型和未绑定类型)。The typeof operator can also be used on a constructed type or an unbound generic type (Bound and unbound types). 未绑定的泛型类型的 @no__t 0 对象与实例类型的 @no__t 1 对象不同。The System.Type object for an unbound generic type is not the same as the System.Type object of the instance type. 实例类型在运行时始终是封闭式构造类型,因此它的 @no__t 0 对象取决于正在使用的运行时类型参数,而未绑定的泛型类型没有类型参数。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]

请注意,int 和 @no__t 为同一类型。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

@No__t-0 和 @no__t 1 运算符用于控制整型算术运算和转换的溢出检查上下文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 ')'
    ;

@No__t-0 运算符在已检查的上下文中计算包含的表达式,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 和 @no__t 语句(checked 和 unchecked 语句)进行控制。The overflow checking context can also be controlled through the checked and unchecked statements (The checked and unchecked statements).

以下操作受 @no__t 0 和 unchecked 运算符和语句所建立的溢出检查上下文影响:The 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.OverflowExceptionOtherwise, 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.

对于非常量表达式(在运行时计算的表达式),该表达式未由任何 @no__t 0 或 @no__t 1 个运算符或语句括起来 @no__t,除非外部因素(如编译器开关和执行环境配置)对 @no__t 进行评估。For 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. 除非将常量表达式显式置于 @no__t 的上下文中,否则在表达式的编译时计算期间发生的溢出将始终导致编译时错误。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.

匿名函数的主体不受 checked 或在其中发生匿名函数的 @no__t 1 上下文影响。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.OverflowExceptionG 方法返回-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). @No__t-0 方法的行为取决于编译的默认溢出检查上下文,但它与 F 相同,或与 @no__t 2 相同。The 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
    }
}

计算 FH 中的常量表达式时出现的溢出将导致报告编译时错误,因为在 @no__t 2 上下文中计算表达式。the 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 中的常量表达式时,也会发生溢出,但由于计算发生在 @no__t 1 上下文中,因此不会报告溢出。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.

@No__t-0 和 @no__t 1 运算符仅对那些在 "(" 和 ")" 标记中包含的那些操作有溢出检查上下文影响。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));
    }
}

使用 F 中 @no__t 0 不会影响对 @no__t 中的 x * y 的计算,因此在默认溢出检查上下文中计算 x * ythe 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.

用十六进制表示法编写带符号整数类型的常量时,@no__t 0 运算符非常方便。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 范围内 @no__t,因此,转换为 int 将产生编译时错误。Because the constants are outside the int range, without the unchecked operator, the casts to int would produce compile-time errors.

@No__t 0 和 @no__t 1 运算符和语句使程序员能够控制某些数值计算的某些方面。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. 例如,两个小数相乘始终导致溢出异常,即使在显式 @no__t 0 构造中也是如此。For example, multiplying two decimals always results in an exception on overflow even within an explicitly unchecked construct. 同样,如果在显式 @no__t 0 构造中,将两个浮点数相乘会导致溢出异常。Similarly, 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是常量表达式: sbytebyteshortushortintuintlongulongchar、0、1 @no__t或任何枚举类型。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_list,则会发生编译时错误。It is a compile-time error for a named_entity designating a method group to have a type_argument_list. Named_entity_target 的 @no__t 类型为的编译时错误。It is a compile time error for a named_entity_target to have the type dynamic.

Nameof_expression是类型 @no__t 的常量表达式,在运行时不起作用。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

@No__t,`+`,`-`,`!`,`~`,`++`,`--`,cast 和 @no__t 7 运算符称为一元运算符。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? ')'
    ;

操作列表可以包括成员访问和元素访问操作(可能自身为空条件)以及调用。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_expression 的null_conditional_expression和 @no__t null_conditional_operations的 @no__t (null 条件元素访问),?.c (null 条件成员访问)和 @no__t 7 (调用)。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).

对于 null_conditional_expression 为primary_expressionE P,让 E0 是通过从 @no__t 7 的每个null_conditional_operations中删除领先的 @no__t 5 的表达式,有一个。For 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. 从概念上讲,@no__t 为,如果 ? 表示的 null 检查都没有找到 @no__t,则将计算该表达式。Conceptually, E0 is the expression that will be evaluated if none of the null checks represented by the ?s do find a null.

此外,让 E1 是通过从 E 中的第一个null_conditional_operations中删除前导 @no__t 而获得的表达式。Also, let E1 be the expression obtained by textually removing the leading ? from just the first of the null_conditional_operations in E. 这可能会导致主表达式(如果只有一个 ?)或其他null_conditional_expressionThis may lead to a primary-expression (if there was just one ?) or to another null_conditional_expression.

例如,如果 @no__t 为 @no__t 的表达式为-1,则 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 用于确定 @no__t 的含义:E0 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.

  • 否则,请让 @no__t 为 @no__t 的类型。Otherwise, 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 的值类型,则 @no__t 的类型为 T0?E 的含义与If 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 的测试,直到没有更多 @no__t,并将表达式一直缩小到主表达式 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 和 @no__t 只计算一次。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 和 @no__t 只计算一次。except that a.b and a.b[0] are evaluated only once.

空条件表达式作为投影初始值设定项Null-conditional expressions as projection initializers

如果在anonymous_object_creation_expression中,只允许使用 null 条件表达式作为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-conditional expressions as statement expressions

如果以 null 条件表达式结束,则只允许将其作为statement_expressionexpression 语句)。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. 然后, expression 语句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);
    

    计算结果时,将 @no__t 0 减去零。The result is computed by subtracting x from zero. 如果 x 的值为操作数类型的最小可表示值(对于 @no__t 为-1,则为-2 ^ 31,对于 long 为-2 ^ 63),则不能在操作数类型内表示 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. 如果 @no__t 为 NaN,则结果也为 NaN。If x is NaN, the result is also NaN.

  • 小数求反:Decimal negation:

    decimal operator -(decimal x);
    

    计算结果时,将 @no__t 0 减去零。The result is computed by subtracting x from zero. Decimal 求反等效于使用类型为 System.Decimal 的一元负运算符。Decimal 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,则结果 @no__t 为-1。This operator computes the logical negation of the operand: If the operand is true, the result is false. 如果操作数为 false,则结果 @no__t 为-1。If 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);

对于每个运算符,操作的结果是 @no__t 的按位求补。For 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) 完全相同,只是在 @no__t 的上下文中,转换为 E 始终按如下方式执行:选中和未选中的运算符)。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.

如果前缀递增或递减运算的操作数是属性或索引器访问,则属性或索引器必须同时具有 @no__t 0 和 @no__t 取值函数。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. 预定义 ++-- 运算符对于以下类型存在: sbytebyteshortushortintuintlongulong、0、1、2、3 和任意枚举类型。Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. 预定义的 @no__t 0 运算符返回通过向操作数添加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.OverflowExceptionIn 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.

@No__t-0 或 --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.
    • 运算符返回的值存储在 @no__t 的计算所得的位置。The 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:
    • 实例表达式(如果 @no__t 0 不 static)和参数列表(如果 x 是索引器访问)与 x 相关联,并在后续的 @no__t 4 和 @no__t 访问器调用中使用结果。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.
    • 调用 x 的 @no__t 取值函数。The get accessor of x is invoked.
    • 使用 get 取值函数返回的值作为其参数调用所选运算符。The selected operator is invoked with the value returned by the get accessor as its argument.
    • 使用由运算符返回的值作为其 value 参数调用 x 的 @no__t 取值函数。The 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.

@No__t 0 和 @no__t 1 运算符还支持后缀表示法(后缀递增和递减运算符)。The ++ and -- operators also support postfix notation (Postfix increment and decrement operators). 通常情况下,@no__t @no__t 的结果是在操作之前 x 的值,而 ++x--x 的结果是操作后 x 的值。Typically, 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.

可以使用后缀或前缀表示法来调用 @no__t 0 或 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
    ;

格式为 (T)Ecast_expression ,其中 T 是一个类型E 是一个unary_expression,它执行 E 到类型 @no__t 的显式转换(显式转换)。A 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. 如果不存在从 ET 的显式转换,则发生绑定时错误。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. 即使 E 表示变量,结果也始终归类为值。The 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),或解释为与parenthesized_expression组合的additive_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 tokens (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.
  • 标记序列对于类型是正确的语法,紧跟在右括号后面的标记是标记 "~"、令牌 "!"、令牌 "("、标识符Unicode 字符转义序列)、文本文本)或除 0 和 1 之外的任何关键字关键字)。The 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.

从消除歧义规则开始,如果 @no__t 为0,y 为标识符,(x)y(x)(y)(x)(-y)cast_expression,则即使 x 标识一个类型,也是如此。From the disambiguation rule it follows that, if x and y are identifiers, (x)y, (x)(y), and (x)(-y) are cast_expressions, but (x)-y is not, even if x identifies a type. 但是,如果 @no__t 为标识预定义类型(如 int)的关键字,则所有这四种形式都是cast_expression的(因为这种关键字本身不可能是表达式)。However, if x is a keyword that identifies a predefined type (such as int), then all four forms are cast_expressions (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 (Iterators). 在最近的封闭 async 函数内, 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
  • 在不安全的上下文中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的操作数称为任务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
  • @no__t 的可访问实例或扩展方法 @no__t 为-1,无参数,没有任何类型参数,并且返回类型 @no__t 为-2,其中所有以下保留: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 (对于简洁起见,则称为 INotifyCompletionA implements the interface System.Runtime.CompilerServices.INotifyCompletion (hereafter known as INotifyCompletion for brevity)
    • A 具有可读的可读实例属性 @no__t 类型 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

@No__t-0 方法的用途是获取任务的awaiterThe 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.

@No__t-0 属性的目的是确定任务是否已完成。The purpose of the IsCompleted property is to determine if the task is already complete. 如果是这样,则无需暂停评估。If so, there is no need to suspend evaluation.

@No__t-0 方法的用途是向任务注册 "延续";也就是说,将在任务完成后调用的委托(类型为 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.

@No__t-0 方法的用途是在任务完成后获取任务的结果。The purpose of the GetResult method is to obtain the outcome of the task once it is complete. 此结果可能会成功完成(可能包含结果值),也可能是由 GetResult 方法引发的异常。This 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(). 因此,如果 @no__t 的返回类型为 void,则将await_expression分类为 nothing。Thus, if the return type of GetResult is void, the await_expression is classified as nothing. 如果它具有非 void 返回类型 T,则将await_expression分类为类型 T 的值。If 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:

  • Awaiter a 通过计算表达式 (t).GetAwaiter() 获得。An awaiter a is obtained by evaluating the expression (t).GetAwaiter().
  • @No__t-0 b 通过计算表达式 (a).IsCompleted 获取。A bool b is obtained by evaluating the expression (a).IsCompleted.
  • 如果 b false,则计算结果取决于 a 是否实现了接口 System.Runtime.CompilerServices.ICriticalNotifyCompletion (对于简洁起见,此称为 @no__t)。If b is false then evaluation depends on whether a implements the interface System.Runtime.CompilerServices.ICriticalNotifyCompletion (hereafter known as ICriticalNotifyCompletion for brevity). 此检查是在绑定时进行的;例如,在运行时,如果 a 的编译时间类型 @no__t 为-1,则在编译时,否则为。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 (Iterators):
    • 如果 a 未实现 ICriticalNotifyCompletion,则计算 (a as (INotifyCompletion)).OnCompleted(r) 的表达式。If a does not implement ICriticalNotifyCompletion, then the expression (a as (INotifyCompletion)).OnCompleted(r) is evaluated.
    • 如果 @no__t 0 实现了 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.
  • 紧随之后(如果 b @no__t 为-1),或在以后调用恢复委托(如果 @no__t 为 b),则计算表达式 (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.OnCompletedICriticalNotifyCompletion.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

@No__t-0、`/`、`%`、@no__t 和 @no__t 运算符称为算术运算符。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,以下所述的解决方法将使用编译时类型为 dynamic 的操作数的运行时类型在运行时进行。In 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. 运算符都计算 xy 的乘积。The 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.OverflowExceptionIn 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. @no__t 为 @no__t 的结果。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);
    

    如果生成的值太大而无法表示为 decimal 格式,则会引发 System.OverflowExceptionIf 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.

    小数乘法等效于使用类型为 System.Decimal 的乘法运算符。Decimal 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. 运算符都计算 xy 的商。The 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.DivideByZeroExceptionIf 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.

    如果左操作数是最小的可表示 intlong 值,右操作数为 -1,则发生溢出。If the left operand is the smallest representable int or long value and the right operand is -1, an overflow occurs. 在 @no__t 0 上下文中,这将导致引发 @no__t (或其子类)。In a checked context, this causes a System.ArithmeticException (or a subclass thereof) to be thrown. 在 @no__t 0 上下文中,它的实现定义为:是否引发了 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. @no__t 为 @no__t 的结果。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.DivideByZeroExceptionIf the value of the right operand is zero, a System.DivideByZeroException is thrown. 如果生成的值太大而无法表示为 decimal 格式,则会引发 System.OverflowExceptionIf 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.Decimal 的除法运算符。Decimal 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. 运算符都计算 xy 之间的相除余数。The 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);
    

    @No__t 的结果为 x - (x / y) * y 生成的值。The result of x % y is the value produced by x - (x / y) * y. 如果 @no__t 为零,则将引发 System.DivideByZeroExceptionIf y is zero, a System.DivideByZeroException is thrown.

    如果左操作数是最小的 int 或 @no__t 值,右操作数 @no__t 为-2,则将引发 @no__t。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. z 是 @no__t 的结果,并计算为 x - n * y,其中 n 是小于或等于 @no__t 的最大整数。z 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 是最接近 @no__t 的整数)。This 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.DivideByZeroExceptionIf the value of the right operand is zero, a System.DivideByZeroException is thrown. 在进行舍入之前,结果的小数位数是两个操作数的刻度中较大的值,如果非零,则结果的符号与 x 相同。The 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.Decimal 的余数运算符。Decimal 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. 当一个或两个操作数为字符串类型时,预定义的加法运算符将连接操作数的字符串表示形式。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.OverflowExceptionIn 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);
    

    Sum 根据 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 是非零的有限值,z 是 @no__t 的结果。In the table, x and y are nonzero finite values, and z is the result of x + y. 如果 @no__t 0 和 y 的量相同但符号相反,z 为正零。If x and y have the same magnitude but opposite signs, z is positive zero. 如果 x + y 太大而无法在目标类型中表示,z 是一个无穷大,其符号与 x + y 相同。If 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 addition:

    decimal operator +(decimal x, decimal y);
    

    如果生成的值太大而无法表示为 decimal 格式,则会引发 System.OverflowExceptionIf 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.Decimal 的加法运算符。Decimal addition is equivalent to using the addition operator of type System.Decimal.

  • 枚举加法。Enumeration addition. 每个枚举类型都隐式提供以下预定义运算符,其中 E 是枚举类型,U 是 @no__t 的基础类型:Every 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);
    

    二元 @no__t 运算符的这些重载执行字符串串联。These overloads of the binary + operator perform string concatenation. 如果字符串串联的操作数为 null,则替换空字符串。If an operand of string concatenation is null, an empty string is substituted. 否则,通过调用从类型 @no__t @no__t 继承的虚拟0方法,将任何非字符串参数转换为其字符串表示形式。Otherwise, 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.OutOfMemoryExceptionA 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);
    

    二元 + 运算符在两个操作数均为 D 的委托类型时执行委托组合。The 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. 由于 @no__t 0 不是委托类型,因此没有为其定义 operator @ no__t。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. 运算符都从 x 中减去 yThe 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.OverflowExceptionIn 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 是非零的有限值,z 是 @no__t 的结果。In the table, x and y are nonzero finite values, and z is the result of x - y. 如果 @no__t 0 和 y 相等,z 为正零。If x and y are equal, z is positive zero. 如果 x - y 太大而无法在目标类型中表示,z 是一个无穷大,其符号与 x - y 相同。If 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);
    

    如果生成的值太大而无法表示为 decimal 格式,则会引发 System.OverflowExceptionIf 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.

    小数减等效于使用类型为 System.Decimal 的减法运算符。Decimal subtraction is equivalent to using the subtraction operator of type System.Decimal.

  • 枚举减法。Enumeration subtraction. 每个枚举类型都隐式提供以下预定义运算符,其中 E 是枚举类型,U 是 @no__t 的基础类型:Every 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). 换言之,运算符计算 xy 的序号值之间的差异,结果的类型为枚举的基础类型。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);
    

    二元 - 运算符在两个操作数均为 D 的委托类型时执行委托删除。The 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

@No__t 0 和 @no__t 1 运算符用于执行移位运算。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,以下所述的解决方法将使用编译时类型为 dynamic 的操作数的运行时类型在运行时进行。In 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 << countx >> 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);
    

    @No__t-0 运算符将 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);
    

    @No__t-0 运算符将 x 向右移位按如下所述计算的位数。The >> operator shifts x right by a number of bits computed as described below.

    如果 x 的类型为 intlong,则将放弃 @no__t 的低序位,其余位将向右移动,如果 @no__t 为非负值,则将高顺序空位位置设置为零; 如果 @no__t 为负数,则设置为1。When 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 的类型为 uintulong,则会丢弃 @no__t 的低序位,其余位将向右移动,而高阶空位位置设置为零。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:

  • 当 @no__t 的类型 @no__t 为-1 或 uint 时,移位计数由 @no__t 3 的低序位5位提供。When the type of x is int or uint, the shift count is given by the low-order five bits of count. 换句话说,移位计数由 @no__t 计算得出。In other words, the shift count is computed from count & 0x1F.
  • 当 @no__t 的类型 @no__t 为-1 或 ulong 时,移位计数由 count 的低序位六位给定。When the type of x is long or ulong, the shift count is given by the low-order six bits of count. 换句话说,移位计数由 @no__t 计算得出。In other words, the shift count is computed from count & 0x3F.

如果生成的移位计数为零,则移位运算符只返回 x 的值。If the resulting shift count is zero, the shift operators simply return the value of x.

移位运算从不导致溢出,并在 checkedunchecked 上下文中产生相同的结果。Shift operations never cause overflows and produce the same results in checked and unchecked contexts.

当 @no__t 运算符的左操作数为有符号整数类型时,运算符将执行算术右移,其中操作数的最高有效位(符号位)的值将传播到高阶空位位置。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. 当 @no__t 运算符的左操作数为无符号整数类型时,运算符将执行逻辑向右移位,其中高阶空位位置始终设置为零。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. 例如,如果 @no__t 为 int 类型的变量,则运算 unchecked((int)((uint)x >> y)) 将执行 @no__t 为3的逻辑移位。For 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

@No__t,`!=`,`<><=>=is和 @no__t 7 运算符称为关系和类型测试运算符。</span><span class="sxs-lookup"><span data-stu-id="d85b3-2115">The==,!=,<,>,<=,>=,isandas` 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运算符中介绍了 is 运算符,并在as 运算符中描述了 as 运算符。The is operator is described in The is operator and the as operator is described in The as operator.

@No__t-0、@no__t <>、@no__t 和 @no__t 运算符是比较运算符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,以下所述的解决方法将使用编译时类型为 dynamic 的操作数的运行时类型在运行时进行。In 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 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 如果 x 等于 y,则 true 否则为 @no__ttrue if x is equal to y, false otherwise
x != y 如果 x 不等于 y,则 true,否则为 @no__ttrue if x is not equal to y, false otherwise
x < y 如果 x 小于 y,则 true,否则为 @no__ttrue if x is less than y, false otherwise
x > y 如果 x 大于 y,则 true,否则为 @no__ttrue if x is greater than y, false otherwise
x <= y 如果 @no__t @no__t 小于或等于 y,则为 0; 否则为 @no__ttrue if x is less than or equal to y, false otherwise
x >= y 如果 x 大于或等于 y,则 true,否则为 @no__ttrue 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 值,该值指示特定关系是 @no__t 还是 falseEach 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 时,<><= 和 @no__t 3 运算符不会生成与相对运算符逻辑求反的结果。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. 例如,如果 @no__t 0 和 y 为 NaN,则 x < yfalse,但 @no__t 为 @no__t。For 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);

其中每个运算符比较两个 decimal 操作数的数值,并返回一个 bool 值,该值指示特定关系是 @no__t 还是 falseEach 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. 每个 decimal 比较等效于使用类型为 System.Decimal 的对应关系或相等运算符。Each 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);

如果 x 和 @no__t 均为 true,或者 x 和 @no__t 都为 false,则 == 的结果 @no__t 为-1。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.

如果 x 和 @no__t 均为 true,或者 x 和 @no__t 都为 false,则 != 的结果 @no__t 为-1。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 时,!= 运算符产生与 @no__t 2 运算符相同的结果。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 的结果(其中 @no__t 为-1,y 是一个枚举类型的表达式,该 @no__t 类型的枚举类型为-3,基础类型 @no__t 为-4,op 是比较运算符之一,与评估 ((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 类型的操作数,因此它们适用于未声明适用的 @no__t 1 和 @no__t 2 成员的所有类型。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_typenull 的类型的值。Both 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,其中 @no__t 为type_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.

除非满足这些条件之一,否则将发生绑定时错误。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; 如果两者 @no__t @no__t 都不是派生自另一个,则两个操作数不可能引用同一个对象。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 与 @no__t 进行比较,并且 @no__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();
        ...
    }
}

即使 T 可以表示值类型,也允许使用 x == null 构造,如果 T 是值类型,则只将结果定义为 falseThe 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 == yx != 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

@No__t-0 和 @no__t 1 变量引用包含相同字符的两个不同的 string 实例。The s and t variables refer to two distinct string instances containing the same characters. 第一个比较输出 True,因为当两个操作数的类型为 string 时,将选择预定义的字符串相等运算符(字符串相等运算符)。The first comparison outputs True because the predefined string equality operator (String equality operators) is selected when both operands are of type string. 其余比较 False 的所有输出,因为当两个操作数的类型均为 @no__t 时,将选择预定义的引用类型相等运算符。The 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,因为强制转换会创建对两个不同的装箱 @no__t 1 值实例的引用。outputs 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.
  • 对于在每个字符位置都具有相同长度和相同字符的字符串实例,这两个值都是非空引用。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,则当且仅当两者都 @no__t 时,它们才相等。If 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_expressions 或lambda_expression的计算生成的调用列表项具有相同的(可能为空)已捕获的外部变量实例集=.Invocation list entries produced from evaluation of semantically identical anonymous_method_expressions or lambda_expressions with the same (possibly empty) set of captured outer variable instances are permitted (but not required) to be equal.

相等运算符和 nullEquality operators and null

@No__t 0 和 @no__t 1 运算符允许一个操作数为可以为 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 的类型的表达式,如果运算符重载决策(二元运算符重载决策)未能找到适用的运算符,则结果将改为从 @no__t 的 HasValue 属性进行计算。where 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

@No__t-0 运算符用于动态检查对象的运行时类型与给定类型是否兼容。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 是一个类型,它是一个布尔值,指示是否可以通过引用转换、装箱转换或取消装箱转换将 @no__t 转换为类型 TThe 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:

  • 如果 @no__t,则会发生编译时错误。If E is an anonymous function, a compile-time error occurs
  • 如果 @no__t 为方法组或 @no__t 1 文本,则为; 如果 @no__t 的类型为引用类型或可为 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.
  • 否则,让 @no__t 表示 @no__t 的动态类型,如下所示:Otherwise, let D represent the dynamic type of E as follows:
    • 如果 @no__t 的类型为引用类型,则 E @no__t 为实例引用的运行时类型。If the type of E is a reference type, D is the run-time type of the instance reference by E.
    • 如果 @no__t 的类型是可以为 null 的类型,D 是可以为 null 的类型的基础类型。If the type of E is a nullable type, D is the underlying type of that nullable type.
    • 如果 @no__t 的类型是不可以为 null 的值类型,D 是 @no__t 类型。If the type of E is a non-nullable value type, D is the type of E.
  • 操作的结果取决于 DT,如下所示:The result of the operation depends on D and T as follows:
    • 如果 @no__t 为引用类型,则结果为 true。如果 @no__t @no__t 为同一类型,则结果为 true; 如果 D 是引用类型,并且存在从 @no__t 到 T 的隐式引用转换,或者 D 是从 @no__t 到的装箱转换,则结果为 true到 T 存在。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.
    • 如果 @no__t 是可以为 null 的类型,则如果 D 是 @no__t 的基础类型,则结果为 true。If T is a nullable type, the result is true if D is the underlying type of T.
    • 如果 T 是不可以为 null 的值类型,则如果 @no__t @no__t 为同一类型,则结果为 true。If 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.

请注意,is 运算符不考虑用户定义的转换。Note that user defined conversions, are not considered by the is operator.

As 运算符The as operator

@No__t-0 运算符用于将值显式转换为给定引用类型或可以为 null 的类型。The as operator is used to explicitly convert a value to a given reference type or nullable type. 与强制转换表达式(强制转换表达式)不同,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 T 形式的操作中,E 必须是表达式,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:

如果 @no__t 的编译时类型不 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.

如果 @no__t 的编译时类型为 dynamic,与强制转换运算符不同,则不会动态绑定 @no__t 2 运算符(动态绑定)。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 运算符一起使用,应改为使用强制转换表达式执行。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 
    }
}

已知 @no__t 的类型参数 @no__t 为-1,因为它具有类约束。the type parameter T of G is known to be a reference type, because it has the class constraint. 不过,类型参数 U 的 @no__t 不是;因此,不允许在 @no__t 中使用 @no__t 2 运算符。The type parameter U of H is not however; hence the use of the as operator in H is disallowed.

逻辑运算符Logical operators

@No__t-0、@no__t 和 @no__t 2 运算符称为逻辑运算符。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,以下所述的解决方法将使用编译时类型为 dynamic 的操作数的运行时类型在运行时进行。In 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 的操作,其中 @no__t 为逻辑运算符之一,将应用重载决策(二元运算符重载决策)来选择特定的运算符实现。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);

@No__t-0 运算符计算两个操作数的按位逻辑 AND,@no__t 2 运算符计算两个操作数的按位逻辑 @no__t,第4个运算符计算两个操作数的按位逻辑 @no__t 异或运算。The & 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 的结果(其中 @no__t 为-1,y 是一个枚举类型的表达式,该 @no__t 类型的枚举类型为-3,基础类型 @no__t 为-4,而 @no__t 为逻辑运算符之一,这与评估 @no__t 6 完全相同。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 逻辑运算符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 或 @no__t @no__t 为-4 @no__t,则 true 的结果。The result of x | y is true if either x or y is true. 否则,结果为 falseOtherwise, the result is false.

如果 @no__t 为-@no__t 2,@no__t 为 false,则 x ^ y 的结果为 true,否则 @no__t 为 falseytrueThe 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 时,^ 运算符将计算与 @no__t 2 运算符相同的结果。When the operands are of type bool, the ^ operator computes the same result as the != operator.

可以为 null 的布尔逻辑运算符Nullable boolean logical operators

可以为 null 的布尔类型 bool? 可以表示三个值: truefalsenull,在概念上类似于用于 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? 操作数的 @no__t 0 和 | 运算符产生的结果与 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);

下表列出了这些运算符为 truefalsenull 的所有值组合所产生的结果。The 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

@No__t 0 和 @no__t 1 运算符称为条件逻辑运算符。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
    ;

@No__t 0 和 @no__t 1 运算符是 &| 运算符的条件版本:The && and || operators are conditional versions of the & and | operators:

  • 操作 x && y 对应于 x & y 的操作,不同之处在于 y 仅在 @no__t 3 不 @no__t 时才进行计算。The 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 仅在 @no__t 3 不 @no__t 时才进行计算。The 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,以下所述的解决方法将使用编译时类型为 dynamic 的操作数的运行时类型在运行时进行。In 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 && yx || y 的操作将通过应用重载决策(二元运算符重载决策)来处理,就像将操作写入 x & y 还是 x | 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,

不能直接重载条件逻辑运算符。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

如果 @no__t 0 或 @no__t 的操作数的类型为 bool,或者操作数的类型未定义适用的 operator & 或 @no__t,但确实定义了到 @no__t 的隐式转换,则按如下方式处理操作::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,并将其转换为 bool 类型。In other words, x is first evaluated and converted to type bool. 如果 @no__t 0 @no__t 为-1,则计算 y,并将其转换为类型 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,并将其转换为 bool 类型。In other words, x is first evaluated and converted to type bool. 如果 x @no__t 为-1,则操作的结果为 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

如果 @no__t 0 或 || 的操作数是声明适用的用户定义的 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. 换言之,运算符必须计算 @no__t 为2的两个操作数的逻辑 AND 或逻辑 OR,并且必须返回 T 类型的结果。In 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 trueoperator false 的声明。T must contain declarations of operator true and operator false.

如果不满足上述任一要求,则会发生绑定时错误。A binding-time error occurs if either of these requirements is not satisfied. 否则,通过将用户定义的 operator true 或 @no__t 3 与选定的用户定义运算符相结合来计算 &&|| 运算: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) 是在 T 中声明的 @no__t 的调用,而 @no__t 为所选的 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,@no__t 并在结果上调用-1,以确定 x 是否确实为 false。In other words, x is first evaluated and operator false is invoked on the result to determine if x is definitely false. 如果 @no__t 为 true,则运算结果为先前为 @no__t 计算得出的值。Then, if x is definitely false, the result of the operation is the value previously computed for x. 否则,将计算 y,并对之前为 x 计算的值调用所选 operator &,并对为 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) 是在 T 中声明的 @no__t 的调用,而 @no__t 为所选的 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,以确定 @no__t 是否确实为 true。In other words, x is first evaluated and operator true is invoked on the result to determine if x is definitely true. 如果 @no__t 为 true,则运算结果为先前为 @no__t 计算得出的值。Then, if x is definitely true, the result of the operation is the value previously computed for x. 否则,将计算 y,并对之前为 x 计算的值调用所选 operator |,并对为 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 trueoperator false 的类型的示例,请参阅数据库布尔类型For an example of a type that implements operator true and operator false, see Database boolean type.

Null 合并运算符The null coalescing operator

@No__t 0 运算符称为 null 合并运算符。The ?? operator is called the null coalescing operator.

null_coalescing_expression
    : conditional_or_expression
    | conditional_or_expression '??' null_coalescing_expression
    ;

@No__t-0 形式的 null 合并表达式需要 @no__t 为可以为 null 的类型或引用类型。A null coalescing expression of the form a ?? b requires a to be of a nullable type or reference type. 如果 @no__t 为非 null,则 a ?? b 的结果为 a;否则,结果为 bIf a is non-null, the result of a ?? b is a; otherwise, the result is b. 仅当 a 为 null 时,操作才计算 bThe 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.

表达式 @no__t 的类型取决于在操作数上可用的隐式转换。The type of the expression a ?? b depends on which implicit conversions are available on the operands. 按照优先顺序,@no__t 的类型为 A0AB,其中 @no__t 为 a 的类型(前提是 @no__t 有一个类型),B 是 @no__t 的类型(前提是 b 具有类型)如果 2 是可以为 null 的类型,则为,0 为基础 @no__t 类型,否则为 0。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 ?? bSpecifically, 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.
  • 如果 @no__t 为动态表达式,则结果类型 @no__t 为-1。If b is a dynamic expression, the result type is dynamic. 在运行时,首先计算 @no__t 0。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 的类型并且存在 @no__t 从-1 到 A0 的隐式转换,则结果类型为 A0Otherwise, if A exists and is a nullable type and an implicit conversion exists from b to A0, the result type is A0. 在运行时,首先计算 @no__t 0。At run-time, a is first evaluated. 如果 @no__t 为 not 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 存在并且存在从 bA 的隐式转换,则结果类型为 AOtherwise, if A exists and an implicit conversion exists from b to A, the result type is A. 在运行时,首先计算 @no__t 0。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 的类型 @no__t 为-1,并且存在从 aB 的隐式转换,则结果类型为 BOtherwise, if b has a type B and an implicit conversion exists from a to B, the result type is B. 在运行时,首先计算 @no__t 0。At run-time, a is first evaluated. 如果 @no__t 为 not null,a 将解包为类型 A0 (如果 @no__t 为,且可以为 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.
  • 否则,@no__t 0 和 @no__t 不兼容,并发生编译时错误。Otherwise, a and b are incompatible, and a compile-time error occurs.

条件运算符Conditional operator

@No__t 0 运算符称为条件运算符。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 的条件表达式首先计算 b 的条件。A conditional expression of the form b ? x : y first evaluates the condition b. 如果 b @no__t 为-1,则计算 x,并成为操作的结果。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).

@No__t 运算符的第一个操作数必须是可以隐式转换为 @no__t 的表达式,或者是实现 @no__t 的类型的表达式。The 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.

第二个和第三个操作数 @no__t @no__t ?: 运算符的第二个和第三个操作数控制条件表达式的类型。The second and third operands, x and y, of the ?: operator control the type of the conditional expression.

  • 如果 x 具有类型 Xy 的类型为 Y,则If x has type X and y has type Y then
    • 如果从 XY 但不是从 YX 的隐式转换(隐式转换),则 @no__t 为条件表达式的类型。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 但不是从 XY 的隐式转换(隐式转换),则 @no__t 为条件表达式的类型。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.
  • 如果 @no__t 0 和 y 中只有一个具有类型,并且的 @no__t 和 @no__t 均可隐式转换为该类型,则这是条件表达式的类型。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.

@No__t-0 形式的条件表达式的运行时处理包括以下步骤:The run-time processing of a conditional expression of the form b ? x : y consists of the following steps:

  • 首先,将计算 b,并决定 b 的 @no__t 值:First, b is evaluated, and the bool value of b is determined:
    • 如果存在从 @no__t 类型到 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.
    • 否则,将调用由 @no__t 类型定义的 @no__t 0-1,以生成 @no__t 2 值。Otherwise, the operator true defined by the type of b is invoked to produce a bool value.
  • 如果上面的步骤生成的 @no__t 0 值为 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_expressions 和anonymous_method_expressions。For historical reasons there are two syntactic flavors of anonymous functions, namely lambda_expressions and anonymous_method_expressions. 几乎所有情况下, lambda_expressionanonymous_method_expression更简洁、更具可表达性,这仍然是用于向后兼容的语言。For almost all purposes, lambda_expressions are more concise and expressive than anonymous_method_expressions, 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
    ;

@No__t 0 运算符具有与赋值(=)相同的优先级,并且是右结合运算符。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 Iterators.

匿名函数的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. 如果不是,则可以使用任何参数列表(不包含 out 参数)转换为委托的匿名函数。If 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_expressions 和anonymous_method_expressions 的行为相同,但以下点除外:The behavior of lambda_expressions and anonymous_method_expressions is the same except for the following points:

  • anonymous_method_expression允许完全省略参数列表,从而 convertibility 委托任何值参数列表的类型。anonymous_method_expressions permit the parameter list to be omitted entirely, yielding convertibility to delegate types of any list of value parameters.
  • lambda_expression允许省略和推断参数类型,而anonymous_method_expression则需要显式声明参数类型。lambda_expressions permit parameter types to be omitted and inferred whereas anonymous_method_expressions 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_expressions 才会转换为兼容的表达式树类型(表达式树类型)。Only lambda_expressions 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_expressionIt 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,则兼容委托类型集和表达式树类型将被限制为没有 out 参数的类型。If 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.
  • 除了在最近的封闭匿名函数的签名(如果有)中指定的 @no__t 0 或 @no__t 1 参数外,正文还需要一个编译时错误,以访问 @no__t 2 或 @no__t 参数。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.
  • 如果 @no__t 的类型为结构类型,则主体将访问 @no__t 的编译时错误。When the type of this is a struct type, it is a compile-time error for the body to access this. 无论访问是显式的(如 this.x)还是隐式的,都是如此(如在 x 中,其中 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.
  • 正文中的 @no__t 0 语句从最近的封闭匿名函数(而不是来自封闭函数成员)的调用返回控制权。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;
    }
}

@No__t 0 类有两个 @no__t 的方法。The ItemList<T> class has two Sum methods. 每个都采用一个 @no__t 0 参数,该参数从列表项中提取要求和的值。Each takes a selector argument, which extracts the value to sum over from a list item. 提取的值可以是 intdouble,所得的总和也可以是 int 或 @no__t 3。The 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 时,这两 @no__t 种方法都适用,因为 d => d. UnitCount 的匿名函数与 @no__t 3 和 @no__t 都兼容。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 的情况下,只有第二个 @no__t 方法适用,因为 d => d.UnitPrice * d.UnitCount 的匿名函数会生成类型 @no__t 为3的值。In 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. 在类的实例函数成员中,@no__t 值被视为值参数,是函数成员内包含的任何匿名函数的外部变量。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. 因此,任何采用已捕获外部变量地址的 @no__t 0 代码都必须首先使用 @no__t 1 语句来修复变量。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;
        ...
    }
}

但是,将 x 的声明移到循环外将导致单个 @no__t 实例化-1:However, 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;
}

这三个委托捕获 @no__t 的同一实例-0 但 @no__t 为1的单独实例,输出为: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 子句开头,以 @no__t 或 @no__t 2 子句结束。A query expression begins with a from clause and ends with either a select or group clause. 初始 from 子句后面可以跟零个或多个 fromletwhere、@no__t 或 @no__t 子句。The initial from clause can be followed by zero or more from, let, where, join or orderby clauses. 每个 from 子句都是引入一个范围变量的生成器,该变量范围内是序列的元素。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 子句根据指定的条件对项进行排序。最后 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. 具体来说,这些是 fromwherejoinonequalsintoletorderbyascendingdescending,0,2。Specifically 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). 具体而言,查询表达式被转换为名为 WhereSelectSelectManyJoinGroupJoinOrderByOrderByDescendingThenByThenByDescendingGroupBy,0 的方法调用。这些方法应具有特定的签名和结果类型,如查询表达式模式中所述。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.

某些翻译注入范围变量,其中包含由 @no__t 表示的透明标识符。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 ... ) ...

以下部分中的翻译假设查询没有 @no__t 的继续符。The 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

显式指定范围变量类型的 @no__t 0 子句A from clause that explicitly specifies a range variable type

from T x in e

转换为is translated into

from x in ( e ) . Cast < T > ( )

显式指定范围变量类型的 @no__t 0 子句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")

显式范围变量类型可用于查询实现非泛型 @no__t 0 接口的集合,但不能用于泛型 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 的类型 @no__t 为-1,则会出现这种情况。In 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. 因此,此步骤通过显式调用源 Select 来保护直接在源代码中编写的退化查询。Therefore this step protects degenerate queries written directly in source code by explicitly calling Select on the source. 然后由 @no__t 的实施者和其他查询运算符构成,以确保这些方法从不返回源对象本身。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 子句后跟除 select 子句之外的其他内容:A 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 } )
...

带有 @no__t 子句的查询表达式A 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 } )
...

带有 @no__t 子句的查询表达式A 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 子句且不包含 select 子句的 @no__t 的查询表达式A 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 子句 @no__t 且后跟 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 子句且后跟除 select 子句之外的某个 @no__t 的查询表达式A 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 })
...

带有 @no__t 子句的查询表达式A 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 )
...

如果 order 子句指定 @no__t 0 方向指示器,则改为生成 @no__t 为1或 ThenByDescending 的调用。If an ordering clause specifies a descending direction indicator, an invocation of OrderByDescending or ThenByDescending is produced instead.

以下翻译假设没有 letwherejoin 或 @no__t 3 子句,且每个查询表达式中的第一个 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 })

其中 x、@no__t 为-1,z 是编译器生成的标识符,否则不可见且无法访问。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> 之间的推荐关系,它确保 @no__t 2 和 @no__t 方法仅对 @no__t 或 @no__t 的结果可用。Notice 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 的结果的建议形状(序列序列),其中每个内部序列都有一个额外的 @no__t 属性。Also notice the recommended shape of the result of GroupBy -- a sequence of sequences, where each inner sequence has an additional Key property.

@No__t 命名空间为实现 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.

@No__t 0 运算符称为简单赋值运算符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.

除 @no__t 0 运算符以外的赋值运算符称为复合赋值运算符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.

使用事件访问表达式作为左操作数的 @no__t 0 和 @no__t 1 运算符称为事件赋值运算符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

@No__t 0 运算符称为简单赋值运算符。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,并且基于 @no__t 的运行时类型,将在运行时进行以下描述的解决方法。In 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.

@No__t-0 形式的简单分配的运行时处理包括以下步骤: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,并在需要时通过隐式转换转换为 @no__t 的类型(隐式转换)。y is evaluated and, if required, converted to the type of x through an implicit conversion (Implicit conversions).
    • 如果 @no__t 给定的变量是reference_type的数组元素,则执行运行时检查,以确保为 y 计算的值与 @no__t 为元素的数组实例兼容。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. 如果 y @no__t 为-1,或者存在从 y 引用的实例的实际元素类型到包含 x 的数组实例的实际元素类型的隐式引用转换(隐式引用转换),则检查成功。The 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.
    • 计算和转换 @no__t 值后得出的值将存储到 @no__t 的计算所给定的位置。The 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:
    • 实例表达式(如果 @no__t 0 不 static)和参数列表(如果 x 是索引器访问)与 x 相关联,并在后续的 @no__t 4 访问器调用中使用结果。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,并在需要时通过隐式转换转换为 @no__t 的类型(隐式转换)。y is evaluated and, if required, converted to the type of x through an implicit conversion (Implicit conversions).
    • 调用 x 的 @no__t 取值函数时,将计算 y 作为其 @no__t 参数。The set accessor of x is invoked with the value computed for y as its value argument.

数组协方差规则(数组协方差)允许数组类型 @no__t 值为对 B[] 的数组类型实例的引用,前提是存在从 BA 的隐式引用转换。The 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

由于不能在 string[] 的元素中存储 @no__t 的实例,因此上一次分配导致引发 System.ArrayTypeMismatchExceptionthe 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.Xp.Yr.A 和 @no__t 3,因为 @no__t 和 @no__t 都是变量。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.Ar.B 不是变量。the 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,并且基于 @no__t 的运行时类型,将在运行时进行以下描述的解决方法。In 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 y 一样。An 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,

  • 如果所选运算符的返回类型可隐式转换为 @no__t 的类型,则运算将计算为 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.
  • 否则,如果所选运算符为预定义运算符,则如果所选运算符的返回类型可显式转换为 @no__t 的类型,并且 y 可隐式转换为 @no__t 的类型,或者该运算符为移位运算符,则为; 否则为移位运算符,则运算计算结果为 x = (T)(x op y),其中 Tx 的类型,只是 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 时,将临时保存 @no__t 的所有构成表达式的结果,并在执行到 x 的赋值时重用。The 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 和 @no__t 是返回 int 的方法,则这些方法只调用一次,顺序 ABCFor 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.

当复合赋值的左操作数是属性访问或索引器访问时,属性或索引器必须同时具有 @no__t 0 访问器和 @no__t 1 访问器。When 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. 存在这样的规则:当左操作数的类型为 sbytebyteshort、@no__t 或 @no__t 时,预定义运算符可用作复合运算符。The 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 yx = y 都允许 x op= 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

如果 @no__t 0 或 -= 运算符的左操作数归类为事件访问,则计算表达式的结果如下: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.
  • 计算 +=-= 运算符的右操作数,并在需要时通过隐式转换转换为左操作数的类型(隐式转换)。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. 如果运算符 +=,则调用 @no__t 1 访问器;如果运算符 -=,则调用 @no__t 3 访问器。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_expressionexpression 语句)的上下文中有效。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
    ;

常量表达式必须是 @no__t 0 文本或具有以下类型之一的值: sbytebyteshortushortintuintlongulong、2、0、1、2、5 或任何枚举类型。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:

  • 文本(包括 @no__t 0 文本)。Literals (including the null literal).
  • 对类类型和结构类型 @no__t 成员的引用。References to const members of class and struct types.
  • 对枚举类型的成员的引用。References to members of enumeration types.
  • 对 @no__t 参数或局部变量的引用References to const parameters or local variables
  • 带括号的子表达式,它们本身就是常量表达式。Parenthesized sub-expressions, which are themselves constant expressions.
  • 如果目标类型是上面列出的类型之一,则强制转换表达式。Cast expressions, provided the target type is one of the types listed above.
  • checkedunchecked 表达式checked and unchecked expressions
  • 默认值表达式Default value expressions
  • Nameof 表达式Nameof expressions
  • 预定义的 +、@no__t @no__t、和 @no__t 一元运算符。The predefined +, -, !, and ~ unary operators.
  • 预定义的 +-*/%<<>>&|^,0,1,2,6,4,5,6,7 二进制运算符,前提是每个操作数都是上面列出的类型。The predefined +, -, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and >= binary operators, provided each operand is of a type listed above.
  • @No__t 的条件运算符。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
}

i 的初始化是错误的,因为需要装箱转换。the initialization of i is an error because a boxing conversion is required. Str 的初始化是错误的,因为需要从非 null 值进行隐式引用转换。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.

除非将常量表达式显式置于 @no__t 0 上下文中,否则在表达式的编译时计算过程中发生的溢出将始终导致编译时错误(常量表达式)。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 类型的常量表达式转换为 sbytebyteshort、@no__t、@no__t 或 @no__t (前提是常量表达式在目标类型的范围内。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 表达式Boolean expressions

Boolean_expression是生成类型为 bool 的结果的表达式。直接或通过在某些上下文中 @no__t 的应用程序,如以下所示。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_statementif 语句)、 while_statementwhile 语句)、 do_statementdo 语句)或for_statement的控制条件表达式(用于语句)是一个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. @No__t 运算符(条件运算符)的控制条件表达式遵循与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 才能生成类型为 bool 的值,如下所示:A 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.
  • 否则,一元运算符重载决策(一元运算符重载决策)用于查找 @no__t 2 上运算符 @no__t 的唯一最佳实现,并且该实现在运行时应用。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 结构类型提供实现 @no__t 和 @no__t 的类型的示例。The DBBool struct type in Database boolean type provides an example of a type that implements operator true and operator false.