表达式Expressions
表达式是运算符和操作数构成的序列。An expression is a sequence of operators and operands. 本章定义语法、操作数和运算符的计算顺序,以及表达式的含义。This chapter defines the syntax, order of evaluation of operands and operators, and meaning of expressions.
表达式分类Expression classifications
表达式分类为以下类别之一:An expression is classified as one of the following:
- 一个 值。A value. 每个值都有关联的类型。Every value has an associated type.
- 一个变量。A variable. 每个变量都有关联的类型,即变量的声明类型。Every variable has an associated type, namely the declared type of the variable.
- 一个命名空间。A namespace. 具有此分类的表达式只能作为 member_access (成员访问) 的左侧出现。An expression with this classification can only appear as the left hand side of a member_access (Member access). 在任何其他上下文中,归类为命名空间的表达式会导致编译时错误。In any other context, an expression classified as a namespace causes a compile-time error.
- 一种类型。A type. 具有此分类的表达式只能作为 member_access (成员访问) 的左侧,或者作为运算符的操作数
as
(为) ,is
运算符 (is operator) ,或 (typeof
typeof 运算符) 的运算符。An expression with this classification can only appear as the left hand side of a member_access (Member access), or as an operand for theas
operator (The as operator), theis
operator (The is operator), or thetypeof
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 bythis
(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. 当访问器 (
get
set
调用实例属性访问的或块) 时,实例表达式的计算结果将成为this
(此访问) 所表示的实例。When an accessor (theget
orset
block) of an instance property access is invoked, the result of evaluating the instance expression becomes the instance represented bythis
(This access). - 事件访问。An event access. 每个事件访问都有关联的类型,即事件的类型。Every event access has an associated type, namely the type of the event. 而且,事件访问可能具有关联的实例表达式。Furthermore, an event access may have an associated instance expression. 事件访问可能显示为和运算符的左操作数
+=
,-=
(事件分配) 。An event access may appear as the left hand operand of the+=
and-=
operators (Event assignment). 在其他任何上下文中,归类为事件访问的表达式会导致编译时错误。In any other context, an expression classified as an event access causes a compile-time error. - 索引器访问。An indexer access. 每个索引器访问都有关联的类型,即索引器的元素类型。Every indexer access has an associated type, namely the element type of the indexer. 此外,索引器访问具有关联的实例表达式和关联的参数列表。Furthermore, an indexer access has an associated instance expression and an associated argument list. 如果调用器 (
get
set
调用索引器访问的或块) ,则实例表达式的计算结果将成为this
(此访问) 所表示的实例,而参数列表的计算结果将成为调用的参数列表。When an accessor (theget
orset
block) of an indexer access is invoked, the result of evaluating the instance expression becomes the instance represented bythis
(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 ofvoid
. 归类为 nothing 的表达式仅在) statement_expression (expression 语句 的上下文中有效。An expression classified as nothing is only valid in the context of a statement_expression (Expression statements).
表达式的最终结果绝不会是命名空间、类型、方法组或事件访问。The final result of an expression is never a namespace, type, method group, or event access. 相反,如上所述,这些类别的表达式是仅在某些上下文中允许的中间构造。Rather, as noted above, these categories of expressions are intermediate constructs that are only permitted in certain contexts.
通过执行 get 访问器 或 set 访问器 的调用,始终可以将属性访问或索引器访问重新分类为值。A property access or indexer access is always reclassified as a value by performing an invocation of the get accessor or the set accessor. 特定访问器由属性或索引器访问的上下文确定:如果访问是赋值的目标,则调用 set 访问器 来 (简单的赋值) 分配新值。The particular accessor is determined by the context of the property or indexer access: If the access is the target of an assignment, the set accessor is invoked to assign a new value (Simple assignment). 否则,将调用 get 访问器 来获取 () 的表达式值的 当前值。Otherwise, the get accessor is invoked to obtain the current value (Values of expressions).
表达式的值Values of expressions
大多数涉及表达式的构造都需要表达式来表示 值。Most of the constructs that involve an expression ultimately require the expression to denote a value. 在这种情况下,如果实际表达式表示命名空间、类型、方法组或不执行任何操作,则会发生编译时错误。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.M
Member 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)
- 重载的一元运算符:
+
、-
、!
、~
、++
、--
true
、、false
Overloaded unary operators:+
,-
,!
,~
,++
,--
,true
,false
- 重载的二元运算符:
+
、-
、*
、/
、%
、&
&&
|
||
??
^
<<
>>
==
!=
>
<
>=
、、、、、、、、、、、、、<=
Overloaded binary operators:+
,-
,*
,/
,%
,&
,&&
,|
,||
,??
,^
,<<
,>>
,==
,!=
,>
,<
,>=
,<=
- 赋值运算符:
=
、+=
、-=
、*=
、/=
、%=
&=
|=
^=
<<=
、、、、、>>=
Assignment operators:=
,+=
,-=
,*=
,/=
,%=
,&=
,|=
,^=
,<<=
,>>=
- 隐式和显式转换Implicit and explicit conversions
当不涉及动态表达式时,c # 默认为静态绑定,这意味着在选择过程中使用构成表达式的编译时类型。When no dynamic expressions are involved, C# defaults to static binding, which means that the compile-time types of constituent expressions are used in the selection process. 但是,当上面列出的操作中的其中一个构成表达式为动态表达式时,将改为动态绑定该操作。However, when one of the constituent expressions in the operations listed above is a dynamic expression, the operation is instead dynamically bound.
绑定时间Binding-time
在编译时进行静态绑定,而动态绑定在运行时发生。Static binding takes place at compile-time, whereas dynamic binding takes place at run-time. 在下面几节中,术语 " 绑定时间 " 指编译时或运行时,具体取决于绑定发生的时间。In the following sections, the term binding-time refers to either compile-time or run-time, depending on when the binding takes place.
下面的示例演示了静态和动态绑定的概念以及绑定时间:The following example illustrates the notions of static and dynamic binding and of binding-time:
object o = 5;
dynamic d = 5;
Console.WriteLine(5); // static binding to Console.WriteLine(int)
Console.WriteLine(o); // static binding to Console.WriteLine(object)
Console.WriteLine(d); // dynamic binding to Console.WriteLine(int)
前两个调用是静态绑定的: Console.WriteLine
根据其自变量的编译时类型选取重载。The first two calls are statically bound: the overload of Console.WriteLine
is picked based on the compile-time type of their argument. 因此,绑定时间为编译时。Thus, the binding-time is compile-time.
第三个调用是动态绑定的: Console.WriteLine
根据其自变量的运行时类型选取重载。The third call is dynamically bound: the overload of Console.WriteLine
is picked based on the run-time type of its argument. 出现这种情况的原因是,参数是动态表达式,其编译时类型为 dynamic
。This 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 typedynamic
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. 运算符的示例包括 +
、-
、_
、/
和 new
。Examples 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 asx++
). - 二元运算符。Binary operators. 二元运算符采用两个操作数,并使用中缀表示法 (如
x + y
) 。The binary operators take two operands and all use infix notation (such asx + y
). - 三元运算符。Ternary operator. 只有一个三元运算符,
?:
exists; 它采用三个操作数,并使用中缀符号 (c ? x : y
) 。Only one ternary operator,?:
, exists; it takes three operands and uses infix notation (c ? x : y
).
表达式中运算符的计算顺序由运算符的 优先级 _ 和 _ 相关性 决定 (运算符优先级和关联 性) 。The order of evaluation of operators in an expression is determined by the precedence _ and _ associativity of the operators (Operator precedence and associativity).
表达式中的操作数从左到右进行计算。Operands in an expression are evaluated from left to right. 例如,在中 F(i) + G(i++) * H(i)
, F
使用旧值调用方法 i
,然后使用 G
旧值调用方法 i
,最后 H
使用的新值调用方法 i
。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)
,因为 *
运算符的优先级高于二进制 +
运算符。For example, the expression x + y _ z
is evaluated as x + (y * z)
because the *
operator has higher precedence than the binary +
operator. 运算符的优先级由其关联的文法产生式的定义来确定。The precedence of an operator is established by the definition of its associated grammar production. 例如, additive_expression 由或运算符分隔的一系列 multiplicative_expression 组成 +
-
,从而使 +
和 -
运算符的优先级低于 *
、 /
和 %
运算符。For example, an additive_expression consists of a sequence of multiplicative_expression s separated by +
or -
operators, thus giving the +
and -
operators lower precedence than the *
, /
, and %
operators.
下表按优先级从高到低的顺序汇总了所有运算符:The following table summarizes all operators in order of precedence from highest to lowest:
节Section | 类别Category | 运算符Operators |
---|---|---|
主要表达式Primary expressions | 主Primary | x.y f(x) a[x] x++ x-- new typeof default checked unchecked delegate x.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) + z
。For example,x + y + z
is evaluated as(x + y) + z
. - 赋值运算符、null 合并运算符和条件运算符 (
?:
) 是 右结合 运算符,这意味着将从右到左执行运算。The assignment operators, the null coalescing operator and the conditional operator (?:
) are right-associative, meaning that operations are performed from right to left. 例如,x = y = z
将计算为x = (y = z)
。For example,x = y = z
is evaluated asx = (y = z)
.
可以使用括号控制优先级和结合性。Precedence and associativity can be controlled using parentheses. 例如,x + y * z
先计算 y
乘 z
,并将结果与 x
相加,而 (x + y) * z
则先计算 x
加 y
,然后将结果与 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
尽管 true
和不 false
是在表达式中显式使用的 (,因此不包括在 运算符优先级和关联 性) 的优先级表中,因为它们是在多个表达式上下文中调用的:布尔表达式 (布尔表达式) 和表达式(涉及条件 (条件运算符) )和条件逻辑运算符 (条件 逻辑运算符) 。Although true
and false
are not used explicitly in expressions (and therefore are not included in the precedence table in Operator precedence and associativity), they are considered operators because they are invoked in several expression contexts: boolean expressions (Boolean expressions) and expressions involving the conditional (Conditional operator), and conditional logical operators (Conditional logical operators).
可 重载的二元运算符 是:The overloadable binary operators are:
+ - * / % & | ^ << >> == != > < >= <=
只能重载上面列出的运算符。Only the operators listed above can be overloaded. 具体而言,无法重载成员访问、方法调用或、、、、、、、、、、、、、、、、、、、 =
&&
||
??
?:
=>
checked
unchecked
new
typeof
default
as
和 is
运算符。In particular, it is not possible to overload member access, method invocation, or the =
, &&
, ||
, ??
, ?:
, =>
, checked
, unchecked
, new
, typeof
, default
, as
, and is
operators.
重载二元运算符时,也会隐式重载相应的赋值运算符(若有)。When a binary operator is overloaded, the corresponding assignment operator, if any, is also implicitly overloaded. 例如,运算符的重载 *
也是运算符的重载 *=
。For example, an overload of operator *
is also an overload of operator *=
. 这将在 复合赋值中进一步说明。This is described further in Compound assignment. 请注意,赋值运算符本身 (=
) 无法重载。Note that the assignment operator itself (=
) cannot be overloaded. 赋值始终将值的简单的逐位副本用于变量。An assignment always performs a simple bit-wise copy of a value into a variable.
强制转换运算(如)是通过向用户定义的转换 (T)x
提供用户定义的转换来重载的) (用户定义的转换 。Cast operations, such as (T)x
, are overloaded by providing user-defined conversions (User-defined conversions).
元素访问(如 a[x]
)不被视为可重载的运算符。Element access, such as a[x]
, is not considered an overloadable operator. 而是通过索引器 (索引器) 来支持用户定义的索引。Instead, user-defined indexing is supported through indexers (Indexers).
在表达式中,运算符是使用运算符表示法引用的,而在声明中,使用函数表示法引用运算符。In expressions, operators are referenced using operator notation, and in declarations, operators are referenced using functional notation. 下表显示了一元运算符和二元运算符的运算符和函数表示法之间的关系。The following table shows the relationship between operator and functional notations for unary and binary operators. 在第一个条目中, op 表示任何可重载的一元前缀运算符。In the first entry, op denotes any overloadable unary prefix operator. 在第二个条目中, op 表示一元后缀 ++
和 --
运算符。In the second entry, op denotes the unary postfix ++
and --
operators. 在第三个条目中, op 表示任何可重载的二元运算符。In the third entry, op denotes any overloadable binary operator.
运算符表示法Operator notation | 函数表示法Functional notation |
---|---|
op x |
operator op(x) |
x op |
operator op(x) |
x op y |
operator op(x,y) |
用户定义的运算符声明始终需要至少一个参数成为包含运算符声明的类或结构类型。User-defined operator declarations always require at least one of the parameters to be of the class or struct type that contains the operator declaration. 因此,用户定义的运算符不能与预定义的运算符具有相同的签名。Thus, it is not possible for a user-defined operator to have the same signature as a predefined operator.
用户定义的运算符声明不能修改运算符的语法、优先级或关联性。User-defined operator declarations cannot modify the syntax, precedence, or associativity of an operator. 例如, /
运算符始终为二元运算符,始终具有在 运算符优先级和关联性中指定的优先级别,并且始终为左结合。For example, the /
operator is always a binary operator, always has the precedence level specified in Operator precedence and associativity, and is always left-associative.
尽管用户定义的运算符可以执行 pleases 的任何计算,但强烈不建议使用生成的实现,而不是所需的结果。While it is possible for a user-defined operator to perform any computation it pleases, implementations that produce results other than those that are intuitively expected are strongly discouraged. 例如,的实现 operator ==
应比较两个操作数的相等性并返回相应的 bool
结果。For example, an implementation of operator ==
should compare the two operands for equality and return an appropriate bool
result.
通过条件逻辑运算符对主要表达式中的单个运算符进行的说明指定运算符的预定义实现以及适用于每个运算符的任何其他规则。The descriptions of individual operators in Primary expressions through Conditional logical operators specify the predefined implementations of the operators and any additional rules that apply to each operator. 这些说明使用术语 一元运算符重载决策 _、 _二进制运算符重载决策*_ 和 _ 数值升级 *,以下部分中提供了这些项的定义。The descriptions make use of the terms unary operator overload resolution _, _binary operator overload resolution_, and _numeric promotion**, definitions of which are found in the following sections.
一元运算符重载决策Unary operator overload resolution
格式为的操作 op x
x op
,其中是可 op
重载的一元运算符, x
是类型的表达式, X
按如下方式进行处理:An operation of the form op x
or x op
, where op
is an overloadable unary operator, and x
is an expression of type X
, is processed as follows:
- 为操作提供的候选用户定义运算符集
X
operator op(x)
是使用 候选用户定义运算符的规则确定的。The set of candidate user-defined operators provided byX
for the operationoperator 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 unaryoperator 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
y
是类型的表达式, Y
按如下方式进行处理:An operation of the form x op y
, where op
is an overloadable binary operator, x
is an expression of type X
, and y
is an expression of type Y
, is processed as follows:
- 确定为操作提供的候选用户定义运算符集
X
Y
operator op(x,y)
。The set of candidate user-defined operators provided byX
andY
for the operationoperator op(x,y)
is determined. 集由提供的候选运算符X
与提供的候选运算符联合组成Y
,每个运算符都是使用 候选用户定义的运算符的规则确定的。The set consists of the union of the candidate operators provided byX
and the candidate operators provided byY
, each determined using the rules of Candidate user-defined operators. 如果X
和Y
是相同的类型,或者如果X
和Y
派生自公共基类型,则仅在组合集内发生一次共享的候选运算符。IfX
andY
are the same type, or ifX
andY
are derived from a common base type, then shared candidate operators only occur in the combined set once. - 如果候选用户定义的运算符集不为空,则这将成为操作的候选运算符集。If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. 否则,预定义的二进制
operator op
实现(包括其提升形式)会成为操作的候选运算符集。Otherwise, the predefined binaryoperator op
implementations, including their lifted forms, become the set of candidate operators for the operation. 给定运算符的预定义实现通过条件逻辑运算符) (算术运算符在运算符说明中指定。The predefined implementations of a given operator are specified in the description of the operator (Arithmetic operators through Conditional logical operators). 对于预定义的枚举和委托运算符,唯一认为的运算符是由作为一个操作数的绑定时类型的枚举或委托类型定义的运算符。For predefined enum and delegate operators, the only operators considered are those defined by an enum or delegate type that is the binding-time type of one of the operands. - 重载决策的重载决策规则应用于候选运算符集,以选择与参数列表相关的最佳运算符
(x,y)
,而此运算符将成为重载决策过程的结果。The overload resolution rules of Overload resolution are applied to the set of candidate operators to select the best operator with respect to the argument list(x,y)
, and this operator becomes the result of the overload resolution process. 如果重载决策未能选择单个最佳运算符,则会发生绑定时错误。If overload resolution fails to select a single best operator, a binding-time error occurs.
候选用户定义的运算符Candidate user-defined operators
给定类型 T
和操作 operator op(A)
,其中是可 op
重载的运算符并且 A
是自变量列表,由为提供的候选用户定义运算符集的 T
operator op(A)
确定如下:Given a type T
and an operation operator op(A)
, where op
is an overloadable operator and A
is an argument list, the set of candidate user-defined operators provided by T
for operator op(A)
is determined as follows:
- 确定类型
T0
。Determine the typeT0
. 如果T
是可以为 null 的类型,T0
则为其基础类型,否则T0
等于T
。IfT
is a nullable type,T0
is its underlying type, otherwiseT0
is equal toT
. - 对于
operator op
T0
以及此类运算符的所有提升形式的所有声明,如果至少有一个运算符适用于与参数列表相关的 (适用的函数成员)A
,则候选运算符集包含中所有此类适用运算符T0
。For alloperator op
declarations inT0
and all lifted forms of such operators, if at least one operator is applicable (Applicable function member) with respect to the argument listA
, then the set of candidate operators consists of all such applicable operators inT0
. - 否则,如果
T0
为object
,则候选运算符集为空。Otherwise, ifT0
isobject
, the set of candidate operators is empty. - 否则,由提供的候选运算符集
T0
是由的直接基类提供的候选运算符集T0
,T0
如果T0
是一个类型参数,则为的有效基类。Otherwise, the set of candidate operators provided byT0
is the set of candidate operators provided by the direct base class ofT0
, or the effective base class ofT0
ifT0
is a type parameter.
数值提升Numeric promotions
数值提升包括自动执行预定义的一元运算符和二元数值运算符的操作数的某些隐式转换。Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. 数值升级不是一种不同的机制,而是将重载决策应用于预定义运算符的效果。Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. 尽管可以实现用户定义的运算符来表现出类似的效果,但具体的数值升级却不会影响用户定义的运算符的计算。Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects.
作为数值升级的一个示例,请考虑二元运算符的预定义实现 *
:As an example of numeric promotion, consider the predefined implementations of the binary *
operator:
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);
当重载决策规则 (重载) 解析 规则应用于这组运算符时,将会影响从操作数类型中存在隐式转换的运算符。When overload resolution rules (Overload resolution) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types. 例如,对于操作 b * s
(其中为, b
byte
而 s
是 short
),重载决策选择 operator *(int,int)
作为最佳运算符。For example, for the operation b * s
, where b
is a byte
and s
is a short
, overload resolution selects operator *(int,int)
as the best operator. 因此,效果为 b
s
,并转换为 int
,并且结果的类型为 int
。Thus, the effect is that b
and s
are converted to int
, and the type of the result is int
. 同样,对于操作 i * d
(其中 i
是 int
和 d
是 double
),重载决策选择 operator *(double,double)
作为最佳运算符。Likewise, for the operation i * d
, where i
is an int
and d
is a double
, overload resolution selects operator *(double,double)
as the best operator.
一元数值促销Unary numeric promotions
一元数值升级针对预定义的 +
、 -
和一元运算符的操作数进行 ~
。Unary numeric promotion occurs for the operands of the predefined +
, -
, and ~
unary operators. 一元数值提升只包含将类型为、、、或的操作数转换 sbyte
byte
short
ushort
char
为类型 int
。Unary numeric promotion simply consists of converting operands of type sbyte
, byte
, short
, ushort
, or char
to type int
. 此外,对于一元 -
运算符,一元数值提升将类型的操作数转换 uint
为类型 long
。Additionally, for the unary -
operator, unary numeric promotion converts operands of type uint
to type long
.
二进制数值升级Binary numeric promotions
对于预定义的、、、、、、、、、、、、 +
-
*
/
%
&
|
^
==
!=
>
<
>=
和 <=
二元运算符的操作数,会发生二进制数值升级。Binary numeric promotion occurs for the operands of the predefined +
, -
, *
, /
, %
, &
, |
, ^
, ==
, !=
, >
, <
, >=
, and <=
binary operators. 二进制数值升级会隐式地将两个操作数转换为通用类型,在非关系运算符的情况下,也会成为运算的结果类型。Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. 二进制数值升级包括应用下列规则,顺序如下:Binary numeric promotion consists of applying the following rules, in the order they appear here:
- 如果任一操作数的类型为
decimal
,则将另一个操作数转换为类型decimal
,或者,如果另一个操作数的类型为或,则会发生绑定时错误float
double
。If either operand is of typedecimal
, the other operand is converted to typedecimal
, or a binding-time error occurs if the other operand is of typefloat
ordouble
. - 否则,如果其中一个操作数为类型
double
,则另一个操作数将转换为类型double
。Otherwise, if either operand is of typedouble
, the other operand is converted to typedouble
. - 否则,如果其中一个操作数为类型
float
,则另一个操作数将转换为类型float
。Otherwise, if either operand is of typefloat
, the other operand is converted to typefloat
. - 否则,如果任一操作数的类型为
ulong
,则将另一个操作数转换为类型ulong
,或者,如果另一个操作数的类型为sbyte
、short
、或,则会发生绑定时错误int
long
。Otherwise, if either operand is of typeulong
, the other operand is converted to typeulong
, or a binding-time error occurs if the other operand is of typesbyte
,short
,int
, orlong
. - 否则,如果其中一个操作数为类型
long
,则另一个操作数将转换为类型long
。Otherwise, if either operand is of typelong
, the other operand is converted to typelong
. - 否则,如果其中一个操作数的类型为
uint
,而另一个操作数的类型为sbyte
、short
或,则int
这两个操作数都将转换为类型long
。Otherwise, if either operand is of typeuint
and the other operand is of typesbyte
,short
, orint
, both operands are converted to typelong
. - 否则,如果其中一个操作数为类型
uint
,则另一个操作数将转换为类型uint
。Otherwise, if either operand is of typeuint
, the other operand is converted to typeuint
. - 否则,两个操作数都将转换为类型
int
。Otherwise, both operands are converted to typeint
.
请注意,第一个规则不允许将 decimal
类型与和类型混合的任何操作 double
float
。Note that the first rule disallows any operations that mix the decimal
type with the double
and float
types. 下面的规则是因为类型与类型之间没有隐式转换 decimal
double
float
。The rule follows from the fact that there are no implicit conversions between the decimal
type and the double
and float
types.
另请注意, ulong
如果另一个操作数的类型为带符号的整数类型,则操作数不能是类型。Also note that it is not possible for an operand to be of type ulong
when the other operand is of a signed integral type. 原因在于,不存在可以表示完整范围 ulong
和有符号整数类型的整数类型。The reason is that no integral type exists that can represent the full range of ulong
as well as the signed integral types.
在上述两种情况下,强制转换表达式可用于将一个操作数显式转换为与另一个操作数兼容的类型。In both of the above cases, a cast expression can be used to explicitly convert one operand to a type that is compatible with the other operand.
示例中In the example
decimal AddPercent(decimal x, double percent) {
return x * (1.0 + percent / 100.0);
}
发生绑定时错误,因为 decimal
不能与进行相乘 double
。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. 如果一个或两个操作数为 null,则提升的运算符将生成 null 值 (异常是该&
类型的和|
运算符bool?
,如) 的 布尔逻辑运算符 中所述。The lifted operator produces a null value if one or both operands are null (an exception being the&
and|
operators of thebool?
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 isbool
. 提升形式是通过?
向每个操作数类型添加单个修饰符来构造的。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. 如果两个操作数都非空,则提升运算符将对操作数进行解包,并应用基础运算符来生成bool
结果。If both operands are non-null, the lifted operator unwraps the operands and applies the underlying operator to produce thebool
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 isbool
. 提升形式是通过?
向每个操作数类型添加单个修饰符来构造的。The lifted form is constructed by adding a single?
modifier to each operand type.false
如果一个或两个操作数为 null,则提升运算符将生成值。The lifted operator produces the valuefalse
if one or both operands are null. 否则,提升运算符将对操作数进行解包,并应用基础运算符来生成bool
结果。Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce thebool
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_name 或 member_access 是 invocation_expression (方法调用) 的 primary_expression ,则称成员被调用。If the simple_name or member_access occurs as the primary_expression of an invocation_expression (Method invocations), the member is said to be invoked.
如果成员是方法或事件,或者它是一个常量、委托类型的字段或属性 (委托) 或 dynamic
(动态类型) 的类型,则认为该成员是 invocable。If a member is a method or event, or if it is a constant, field or property of either a delegate type (Delegates) or the type dynamic
(The dynamic type), then the member is said to be invocable.
成员查找不仅考虑了成员的名称,还考虑了成员具有的类型参数的数目以及该成员是否可访问。Member lookup considers not only the name of a member but also the number of type parameters the member has and whether the member is accessible. 出于成员查找的目的,泛型方法和嵌套泛型类型具有其各自声明中指示的类型参数的数目,并且所有其他成员都有零个类型参数。For the purposes of member lookup, generic methods and nested generic types have the number of type parameters indicated in their respective declarations and all other members have zero type parameters.
N
类型中具有类型参数的名称的成员查找 K
T
按如下方式进行处理:A member lookup of a name N
with K
type parameters in a type T
is processed as follows:
- 首先,确定一组名为的可访问成员
N
:First, a set of accessible members namedN
is determined:- 如果
T
是一个类型参数,则该集是N
在指定为主约束或辅助约束的每个类型中名为的可访问成员集的并集, (类型参数约束)T
,以及中名为的可访问成员集N
object
。IfT
is a type parameter, then the set is the union of the sets of accessible members namedN
in each of the types specified as a primary constraint or secondary constraint (Type parameter constraints) forT
, along with the set of accessible members namedN
inobject
. - 否则,该集由中名为的所有可访问 (成员访问) 成员
N
T
,包括继承成员和中命名的可访问成员N
object
。Otherwise, the set consists of all accessible (Member access) members namedN
inT
, including inherited members and the accessible members namedN
inobject
. 如果T
是构造类型,则通过替换类型参数来获取成员集,如 构造类型成员中所述。IfT
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 anoverride
modifier are excluded from the set.
- 如果
- 接下来,如果
K
为零,则将删除所有声明都包含类型参数的嵌套类型。Next, ifK
is zero, all nested types whose declarations include type parameters are removed. 如果不K
为零,则删除所有具有不同数量的类型参数的成员。IfK
is not zero, all members with a different number of type parameters are removed. 请注意,当K
为零时,不会删除具有类型参数的方法,因为类型推理过程 (类型推理) 或许能够推断类型参数。Note that whenK
is zero, methods having type parameters are not removed, since the type inference process (Type inference) might be able to infer the type arguments. - 接下来,如果 调用 成员,则将从集合中删除所有非 invocable 成员。Next, if the member is invoked, all non-invocable members are removed from the set.
- 接下来,将从集合中删除其他成员隐藏的成员。Next, members that are hidden by other members are removed from the set. 对于集中的每个成员
S.M
,其中S
是声明成员的类型,将M
应用以下规则:For every memberS.M
in the set, whereS
is the type in which the memberM
is declared, the following rules are applied:- 如果
M
是常量、字段、属性、事件或枚举成员,则将从集合中删除在基类型中声明的所有成员S
。IfM
is a constant, field, property, event, or enumeration member, then all members declared in a base type ofS
are removed from the set. - 如果
M
是一个类型声明,则从该集内删除所有在基类型中声明的非类型S
,并M
从该集内删除与基类型中声明的类型参数数目相同的所有类型声明S
。IfM
is a type declaration, then all non-types declared in a base type ofS
are removed from the set, and all type declarations with the same number of type parameters asM
declared in a base type ofS
are removed from the set. - 如果
M
是方法,则将从集合中删除在的基类型中声明的所有非方法成员S
。IfM
is a method, then all non-method members declared in a base type ofS
are removed from the set.
- 如果
- 接下来,将从集合中删除类成员隐藏的接口成员。Next, interface members that are hidden by class members are removed from the set. 仅当
T
是类型参数并且T
既有有效的基类object
,也具有非空的有效接口集 (类型参数约束) 时,此步骤才会起作用。This step only has an effect ifT
is a type parameter andT
has both an effective base class other thanobject
and a non-empty effective interface set (Type parameter constraints). 对于集合中的每个成员S.M
,其中S
是声明成员的类型M
,如果S
是类声明(而不是),则应用以下规则object
:For every memberS.M
in the set, whereS
is the type in which the memberM
is declared, the following rules are applied ifS
is a class declaration other thanobject
:- 如果
M
是常量、字段、属性、事件、枚举成员或类型声明,则会从集中删除在接口声明中声明的所有成员。IfM
is a constant, field, property, event, enumeration member, or type declaration, then all members declared in an interface declaration are removed from the set. - 如果
M
是方法,则将从集合中删除在接口声明中声明的所有非方法成员,并M
从集合中删除具有与在接口声明中声明的签名相同的所有方法。IfM
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 asM
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
为object
,则T
没有基类型。IfT
isobject
, thenT
has no base type. - 如果
T
是 enum_type,则的基类型T
是类类型System.Enum
、System.ValueType
和object
。IfT
is an enum_type, the base types ofT
are the class typesSystem.Enum
,System.ValueType
, andobject
. - 如果
T
是 struct_type,则的基类型T
是类类型System.ValueType
和object
。IfT
is a struct_type, the base types ofT
are the class typesSystem.ValueType
andobject
. - 如果
T
是 class_type,则的基类型T
是的基类T
,包括类类型object
。IfT
is a class_type, the base types ofT
are the base classes ofT
, including the class typeobject
. - 如果
T
是 interface_type,则的基类型T
是T
和类类型的基接口object
。IfT
is an interface_type, the base types ofT
are the base interfaces ofT
and the class typeobject
. - 如果
T
是 array_type,则的基类型T
是类类型System.Array
和object
。IfT
is an array_type, the base types ofT
are the class typesSystem.Array
andobject
. - 如果
T
是 delegate_type,则的基类型T
是类类型System.Delegate
和object
。IfT
is a delegate_type, the base types ofT
are the class typesSystem.Delegate
andobject
.
函数成员Function members
函数成员是包含可执行语句的成员。Function members are members that contain executable statements. 函数成员始终是类型的成员,不能是命名空间的成员。Function members are always members of types and cannot be members of namespaces. C # 定义以下类别的函数成员:C# defines the following categories of function members:
- 方法Methods
- 属性Properties
- 事件Events
- 索引器Indexers
- 用户定义的运算符User-defined operators
- 实例构造函数Instance constructors
- 静态构造函数Static constructors
- 析构函数Destructors
除了不能) 显式调用的析构函数和静态构造函数 (,函数成员中包含的语句是通过函数成员调用来执行的。Except for destructors and static constructors (which cannot be invoked explicitly), the statements contained in function members are executed through function member invocations. 用于编写函数成员调用的实际语法取决于特定的函数成员类别。The actual syntax for writing a function member invocation depends on the particular function member category.
参数列表 (参数 列表) 函数成员调用为函数成员的参数提供实际值或变量引用。The argument list (Argument lists) of a function member invocation provides actual values or variable references for the parameters of the function member.
调用泛型方法时,可以使用类型推理来确定要传递给方法的类型参数集。Invocations of generic methods may employ type inference to determine the set of type arguments to pass to the method. 此过程在 类型推理中进行了介绍。This process is described in Type inference.
调用方法、索引器、运算符和实例构造函数会使用重载决策来确定要调用的候选函数成员集中哪一组。Invocations of methods, indexers, operators and instance constructors employ overload resolution to determine which of a candidate set of function members to invoke. 重载决策中介绍了此过程。This process is described in Overload resolution.
在绑定时确定了特定函数成员(可能通过重载决策)后,调用函数成员的实际运行时过程将在 编译时检查动态重载决策中进行说明。Once a particular function member has been identified at binding-time, possibly through overload resolution, the actual run-time process of invoking the function member is described in Compile-time checking of dynamic overload resolution.
下表总结了构造中发生的处理,涉及到可显式调用的六类函数成员。The following table summarizes the processing that takes place in constructs involving the six categories of function members that can be explicitly invoked. 在表中,、、 e
x
y
和 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) |
应用重载决策,以选择 F 包含类或结构中的最佳方法。Overload resolution is applied to select the best method F in the containing class or struct. 方法是通过参数列表调用的 (x,y) 。The method is invoked with the argument list (x,y) . 如果该方法不为 static ,则实例表达式为 this 。If the method is not static , the instance expression is this . |
T.F(x,y) |
应用重载决策,以选择 F 类或结构中的最佳方法 T 。Overload 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) |
重载决策适用于在类、结构或接口中选择由类型给定的最佳方法 F e 。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 |
get P 调用包含类或结构中的属性的访问器。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. 如果不 P 为 static ,则实例表达式为 this 。If P is not static , the instance expression is this . |
P = value |
set P 包含类或结构中的属性的访问器是通过参数列表调用的 (value) 。The set accessor of the property P in the containing class or struct is invoked with the argument list (value) . 如果是只读的,则会发生编译时错误 P 。A compile-time error occurs if P is read-only. 如果不 P 为 static ,则实例表达式为 this 。If P is not static , the instance expression is this . |
|
T.P |
get P 调用类或结构中的属性的访问器 T 。The get accessor of the property P in the class or struct T is invoked. 如果不 P 是 static 或 P 是只写的,则会发生编译时错误。A compile-time error occurs if P is not static or if P is write-only. |
|
T.P = value |
set 类或结构中的属性的访问器 P T 是通过参数列表调用的 (value) 。The set accessor of the property P in the class or struct T is invoked with the argument list (value) . 如果不 P 是 static 或为只读,则会发生编译时错误 P 。A compile-time error occurs if P is not static or if P is read-only. |
|
e.P |
的 get P 类型提供的类、结构或接口中的属性的访问器由 e 实例表达式调用 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 为, static 或者如果 P 为只写,则发生绑定时错误。A binding-time error occurs if P is static or if P is write-only. |
|
e.P = value |
的 set P 类型提供的类、结构或接口中的属性的访问器 e 是使用实例表达式 e 和参数列表调用的 (value) 。The set accessor of the property P in the class, struct, or interface given by the type of e is invoked with the instance expression e and the argument list (value) . 如果 P 为 static 或 P 为只读,则会发生绑定时错误。A binding-time error occurs if P is static or if P is read-only. |
|
事件访问Event access | E += value |
add E 调用包含类或结构中的事件的访问器。The add accessor of the event E in the containing class or struct is invoked. 如果不 E 是静态的,则实例表达式为 this 。If E is not static, the instance expression is this . |
E -= value |
remove E 调用包含类或结构中的事件的访问器。The remove accessor of the event E in the containing class or struct is invoked. 如果不 E 是静态的,则实例表达式为 this 。If E is not static, the instance expression is this . |
|
T.E += value |
add E 调用类或结构中的事件的访问器 T 。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 |
remove E 调用类或结构中的事件的访问器 T 。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 |
的 add E 类型提供的类、结构或接口中的事件的访问器由 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 |
的 remove E 类型提供的类、结构或接口中的事件的访问器由 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 |
应用重载决策,以选择由类型给定的类或结构中的最佳一元运算符 x 。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 |
重载决策适用于在和类型给定的类或结构中选择最佳的二元运算符 x y 。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. 对于索引器,调用
set
访问器时,自变量列表还包括指定为赋值运算符的右操作数的表达式。For indexers, when invoking theset
accessor, the argument list additionally includes the expression specified as the right operand of the assignment operator. - 对于属性,调用访问器时,参数列表是空的,在
get
调用访问器时,参数列表由指定为赋值运算符的右操作数的表达式组成set
。For properties, the argument list is empty when invoking theget
accessor, and consists of the expression specified as the right operand of the assignment operator when invoking theset
accessor. - 对于事件,参数列表包含指定为 or 运算符的右操作数的表达式
+=
-=
。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_list:The arguments of an instance constructor, method, indexer or delegate invocation are specified as an argument_list:
argument_list
: argument (',' argument)*
;
argument
: argument_name? argument_value
;
argument_name
: identifier ':'
;
argument_value
: expression
| 'ref' variable_reference
| 'out' variable_reference
;
Argument_list 由一个或多个由逗号分隔的 参数 组成。An argument_list consists of one or more argument s, separated by commas. 每个自变量都包含一个可选的 argument_name 后跟一个 argument_value。Each 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 keywordref
followed by a variable_reference (Variable references), indicating that the argument is passed as a reference parameter (Reference parameters). 在将变量作为引用参数传递之前,必须明确地 (明确赋值) 赋值。A variable must be definitely assigned (Definite assignment) before it can be passed as a reference parameter. 关键字out
后跟一个 Variable_reference (变量引用) ,指示参数作为输出参数传递 (输出参数) 。The keywordout
followed by a variable_reference (Variable references), indicating that the argument is passed as an output parameter (Output parameters). 在将变量作为输出参数传递的情况下,变量被视为明确赋值 (明确赋值) 。A variable is considered definitely assigned (Definite assignment) following a function member invocation in which the variable is passed as an output parameter.
对应的参数Corresponding parameters
对于参数列表中的每个参数,必须在要调用的函数成员或委托中包含相应的参数。For each argument in an argument list there has to be a corresponding parameter in the function member or delegate being invoked.
以下内容中使用的参数列表的确定方式如下:The parameter list used in the following is determined as follows:
- 对于在类中定义的虚拟方法和索引器,将从函数成员的最特定声明或重写中选取参数列表,从接收方的静态类型开始,然后搜索其基类。For virtual methods and indexers defined in classes, the parameter list is picked from the most specific declaration or override of the function member, starting with the static type of the receiver, and searching through its base classes.
- 对于接口方法和索引器,从接口类型和搜索基接口开始,选取参数列表作为成员的最明确定义。For interface methods and indexers, the parameter list is picked form the most specific definition of the member, starting with the interface type and searching through the base interfaces. 如果找不到唯一的参数列表,则将构造一个具有不可访问的名称且不带可选参数的参数列表,以便调用不能使用命名参数或省略可选参数。If no unique parameter list is found, a parameter list with inaccessible names and no optional parameters is constructed, so that invocations cannot use named parameters or omit optional arguments.
- 对于分部方法,使用定义分部方法声明的参数列表。For partial methods, the parameter list of the defining partial method declaration is used.
- 对于所有其他函数成员和委托,只有一个参数列表,该列表是使用的。For all other function members and delegates there is only a single parameter list, which is the one used.
参数或参数的位置定义为参数列表或参数列表中其前面的参数或参数的数目。The position of an argument or parameter is defined as the number of arguments or parameters preceding it in the argument list or parameter list.
按如下所示建立函数成员参数的对应参数:The corresponding parameters for function member arguments are established as follows:
- 实例构造函数、方法、索引器和委托的 argument_list 中的参数:Arguments in the argument_list of instance constructors, methods, indexers and delegates:
- 一个位置参数,其中固定参数出现在参数列表中的同一位置,该参数对应于该参数。A positional argument where a fixed parameter occurs at the same position in the parameter list corresponds to that parameter.
- 函数成员的位置自变量,其正常形式中调用的参数数组对应于参数数组,该参数数组必须出现在参数列表中的同一位置。A positional argument of a function member with a parameter array invoked in its normal form corresponds to the parameter array, which must occur at the same position in the parameter list.
- 函数成员的位置自变量,其中的参数数组以其展开形式调用,其中没有固定参数出现在参数列表中的同一位置,它对应于参数数组中的一个元素。A positional argument of a function member with a parameter array invoked in its expanded form, where no fixed parameter occurs at the same position in the parameter list, corresponds to an element in the parameter array.
- 命名参数对应于参数列表中具有相同名称的参数。A named argument corresponds to the parameter of the same name in the parameter list.
- 对于索引器,调用
set
访问器时,指定为赋值运算符的右操作数的表达式对应于value
set
访问器声明的隐式参数。For indexers, when invoking theset
accessor, the expression specified as the right operand of the assignment operator corresponds to the implicitvalue
parameter of theset
accessor declaration.
- 对于属性,在调用
get
访问器时没有参数。For properties, when invoking theget
accessor there are no arguments. 调用set
访问器时,指定为赋值运算符的右操作数的表达式对应于value
访问器声明的隐式参数set
。When invoking theset
accessor, the expression specified as the right operand of the assignment operator corresponds to the implicitvalue
parameter of theset
accessor declaration. - 对于用户定义的一元运算符 (包括转换) ,单个操作数对应于运算符声明的单个参数。For user-defined unary operators (including conversions), the single operand corresponds to the single parameter of the operator declaration.
- 对于用户定义的二进制运算符,左操作数对应于第一个参数,右侧操作数对应于运算符声明的第二个参数。For user-defined binary operators, the left operand corresponds to the first parameter, and the right operand corresponds to the second parameter of the operator declaration.
参数列表的运行时计算Run-time evaluation of argument lists
在运行时处理函数成员调用期间 (对 动态重载解析) 进行编译时检查 ,将按从左至右的顺序计算参数列表的表达式或变量引用,如下所示:During the run-time processing of a function member invocation (Compile-time checking of dynamic overload resolution), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows:
- 对于值参数,将计算参数表达式,并在执行相应参数类型) (隐 式转换。For a value parameter, the argument expression is evaluated and an implicit conversion (Implicit conversions) to the corresponding parameter type is performed. 生成的值将成为函数成员调用中 value 参数的初始值。The resulting value becomes the initial value of the value parameter in the function member invocation.
- 对于引用参数或输出参数,将对变量引用进行计算,并将生成的存储位置成为函数成员调用中的参数所表示的存储位置。For a reference or output parameter, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. 如果作为引用或输出参数提供的变量引用是 reference_type 的数组元素,则执行运行时检查以确保数组的元素类型与参数的类型相同。If the variable reference given as a reference or output parameter is an array element of a reference_type, a run-time check is performed to ensure that the element type of the array is identical to the type of the parameter. 如果此检查失败,
System.ArrayTypeMismatchException
则会引发。If this check fails, aSystem.ArrayTypeMismatchException
is thrown.
方法、索引器和实例构造函数可以将其最右侧的参数声明为参数数组, (参数数组) 。Methods, indexers, and instance constructors may declare their right-most parameter to be a parameter array (Parameter arrays). 此类函数成员的调用方式为其正常形式或采用其展开形式,具体取决于适用 (适用的函数成员) :Such function members are invoked either in their normal form or in their expanded form depending on which is applicable (Applicable function member):
- 当使用参数数组的函数成员以其正常形式调用时,为参数数组提供的参数必须是可隐式转换 () 到参数数组类型的 隐式转换 的单个表达式。When a function member with a parameter array is invoked in its normal form, the argument given for the parameter array must be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. 在这种情况下,参数数组的作用与值参数完全相同。In this case, the parameter array acts precisely like a value parameter.
- 当具有参数数组的函数成员以其展开形式调用时,调用必须为参数数组指定零个或多个位置参数,其中每个参数都是隐式转换 (隐式 转换) 到参数数组的元素类型的表达式。When a function member with a parameter array is invoked in its expanded form, the invocation must specify zero or more positional arguments for the parameter array, where each argument is an expression that is implicitly convertible (Implicit conversions) to the element type of the parameter array. 在这种情况下,调用会创建一个参数数组类型的实例,该实例的长度与参数的数量相对应,使用给定的参数值初始化数组实例的元素,并使用新创建的数组实例作为实参。In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, initializes the elements of the array instance with the given argument values, and uses the newly created array instance as the actual argument.
自变量列表的表达式始终按写入它们的顺序进行计算。The expressions of an argument list are always evaluated in the order they are written. 因此,示例Thus, the example
class Test
{
static void F(int x, int y = -1, int z = -2) {
System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z);
}
static void Main() {
int i = 0;
F(i++, i++, i++);
F(z: i++, x: i++);
}
}
生成输出produces the output
x = 0, y = 1, z = 2
x = 4, y = -1, z = 3
数组协方差规则 (数组协方 差) 允许将数组类型的值 A[]
作为对数组类型的实例的引用 B[]
,前提是从到存在隐式引用转换 B
A
。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, when an array element of a reference_type is passed as a reference or output parameter, a run-time check is required to ensure that the actual element type of the array is identical to that of the parameter. 示例中In the example
class Test
{
static void F(ref object x) {...}
static void Main() {
object[] a = new object[10];
object[] b = new string[10];
F(ref a[0]); // Ok
F(ref b[1]); // ArrayTypeMismatchException
}
}
的第二次调用 F
导致 System.ArrayTypeMismatchException
引发,因为的实际元素类型 b
为 string
,而不是 object
。the second invocation of F
causes a System.ArrayTypeMismatchException
to be thrown because the actual element type of b
is string
and not object
.
在以其展开形式调用具有参数数组的函数成员时,调用的处理方式与数组创建表达式中的数组创建表达式) (数组创建 表达式的数组创建表达式相同。When a function member with a parameter array is invoked in its expanded form, the invocation is processed exactly as if an array creation expression with an array initializer (Array creation expressions) was inserted around the expanded parameters. 例如,给定声明For example, given the declaration
void F(int x, int y, params object[] args);
对方法的展开形式的以下调用the following invocations of the expanded form of the method
F(10, 20);
F(10, 20, 30, 40);
F(10, 20, 1, "hello", 3.0);
完全对应于correspond exactly to
F(10, 20, new object[] {});
F(10, 20, new object[] {30, 40});
F(10, 20, new object[] {1, "hello", 3.0});
特别要注意的是,如果参数数组提供的参数为零,则将创建一个空数组。In particular, note that an empty array is created when there are zero arguments given for the parameter array.
当使用相应的可选参数从函数成员中省略参数时,将隐式传递函数成员声明的默认参数。When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. 因为这些始终是常量,所以其计算不会影响剩余参数的计算顺序。Because these are always constant, their evaluation will not impact the evaluation order of the remaining arguments.
类型推理Type inference
如果在未指定类型实参的情况下调用泛型方法,则 类型推理 进程将尝试推断调用的类型参数。When a generic method is called without specifying type arguments, a type inference process attempts to infer type arguments for the call. 类型推理的存在允许使用更方便的语法来调用泛型方法,并允许程序员避免指定冗余类型信息。The presence of type inference allows a more convenient syntax to be used for calling a generic method, and allows the programmer to avoid specifying redundant type information. 例如,给定方法声明:For example, given the method declaration:
class Chooser
{
static Random rand = new Random();
public static T Choose<T>(T first, T second) {
return (rand.Next(2) == 0)? first: second;
}
}
可以调用 Choose
方法而无需显式指定类型参数:it is possible to invoke the Choose
method without explicitly specifying a type argument:
int i = Chooser.Choose(5, 213); // Calls Choose<int>
string s = Chooser.Choose("foo", "bar"); // Calls Choose<string>
通过类型推理,类型参数 int
和 string
由方法的自变量确定。Through type inference, the type arguments int
and string
are determined from the arguments to the method.
类型推理作为方法调用的绑定时处理的一部分进行, (方法 调用) 并在调用的重载决策步骤之前发生。Type inference occurs as part of the binding-time processing of a method invocation (Method invocations) and takes place before the overload resolution step of the invocation. 如果在方法调用中指定了特定方法组,但没有将任何类型参数指定为方法调用的一部分,则会将类型推理应用于方法组中的每个泛型方法。When a particular method group is specified in a method invocation, and no type arguments are specified as part of the method invocation, type inference is applied to each generic method in the method group. 如果类型推理成功,则使用推断出的类型参数来确定参数的类型,以供后续重载决策使用。If type inference succeeds, then the inferred type arguments are used to determine the types of arguments for subsequent overload resolution. 如果重载决策选择泛型方法作为要调用的方法,则会将推断的类型参数用作调用的实际类型参数。If overload resolution chooses a generic method as the one to invoke, then the inferred type arguments are used as the actual type arguments for the invocation. 如果特定方法的类型推理失败,则该方法将不参与重载决策。If type inference for a particular method fails, that method does not participate in overload resolution. 类型推理的失败本身不会导致绑定时错误。The failure of type inference, in and of itself, does not cause a binding-time error. 但是,当重载决策失败并找不到任何适用的方法时,通常会导致绑定时错误。However, it often leads to a binding-time error when overload resolution then fails to find any applicable methods.
如果所提供的参数数目不同于方法中的参数数量,则推理会立即失败。If the supplied number of arguments is different than the number of parameters in the method, then inference immediately fails. 否则,假定泛型方法具有以下签名:Otherwise, assume that the generic method has the following signature:
Tr M<X1,...,Xn>(T1 x1, ..., Tm xm)
利用形式的方法调用, M(E1...Em)
类型推理的任务是查找每个类型参数的唯一类型参数, S1...Sn
X1...Xn
以便调用 M<S1...Sn>(E1...Em)
变为有效。With a method call of the form M(E1...Em)
the task of type inference is to find unique type arguments S1...Sn
for each of the type parameters X1...Xn
so that the call M<S1...Sn>(E1...Em)
becomes valid.
在推断过程中,每个类型形参 Xi
都 固定 到特定类型或未固定的一 Si
组 边界。 During the process of inference each type parameter Xi
is either fixed to a particular type Si
or unfixed with an associated set of bounds. 每个边界都是某种类型 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
对于每个方法参数 Ei
:For each of the method arguments Ei
:
- 如果
Ei
是匿名函数,则 显式参数类型推理 (显式参数类型 推断,) 从Ei
到Ti
IfEi
is an anonymous function, an explicit parameter type inference (Explicit parameter type inferences) is made fromEi
toTi
- 否则,如果
Ei
具有类型U
并且xi
是值参数,则将 从 到进行 下限推理U
Ti
。Otherwise, ifEi
has a typeU
andxi
is a value parameter then a lower-bound inference is made fromU
toTi
. - 否则,如果
Ei
具有类型U
并且xi
为ref
或参数,out
则将 从 到进行 精确推理U
Ti
。Otherwise, ifEi
has a typeU
andxi
is aref
orout
parameter then an exact inference is made fromU
toTi
. - 否则,不会对此参数进行推理。Otherwise, no inference is made for this argument.
第二阶段The second phase
第二个阶段按如下方式进行:The second phase proceeds as follows:
-
Xi
不 依赖于) 任何 (依赖关系的所有未固定类型变量Xj
都固定 (修复) 。All unfixed type variablesXi
which do not depend on (Dependence) anyXj
are fixed (Fixing). - 如果不存在这样的类型变量,则所有未 固定的类型变量
Xi
都是 固定 的,所有这些变量都是以下类型:If no such type variables exist, all unfixed type variablesXi
are fixed for which all of the following hold:- 至少有一个
Xj
依赖于的类型变量Xi
There is at least one type variableXj
that depends onXi
Xi
具有非空的界限集Xi
has a non-empty set of bounds
- 至少有一个
- 如果不存在这样的类型变量,并且仍有未 固定 的类型变量,则类型推理将失败。If no such type variables exist and there are still unfixed type variables, type inference fails.
- 否则,如果不存在进一步的未 固定 类型变量,则类型推理将成功。Otherwise, if no further unfixed type variables exist, type inference succeeds.
- 否则,对于
Ei
具有相应参数类型的所有参数(Ti
其中 输出 类型 (输出类型) 包含未 固定 的类型变量但输入类型Xj
(输入类型) ),输出类型推理 (从 到进行) 输出类型推断Ei
Ti
。Otherwise, for all argumentsEi
with corresponding parameter typeTi
where the output types (Output types) contain unfixed type variablesXj
but the input types (Input types) do not, an output type inference (Output type inferences) is made fromEi
toTi
. 然后,重复第二阶段。Then the second phase is repeated.
输入类型Input types
如果 E
是方法组或隐式类型的匿名函数,并且 T
是委托类型或表达式树类型,则的所有参数类型 T
均为类型为的 输入类型 E
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
如果 E
是方法组或匿名函数,并且 T
是委托类型或表达式树类型,则的返回类型 T
为类型为 的输出类型 E
T
。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
如果对于类型为的某个参数出现在类型为的 Xj
Ek
Tk
Xj
输入类型 中 Ek
且在类型为 Tk
Xi
的 输出类型 中 Ek
出现 Tk
,则未固定类型变量直接依赖于未固定的类型变量。An unfixed type variable Xi
depends directly on an unfixed type variable Xj
if for some argument Ek
with type Tk
Xj
occurs in an input type of Ek
with type Tk
and Xi
occurs in an output type of Ek
with type Tk
.
Xj
依赖于 Xi
如果 Xj
直接依赖 Xi
,则为; 如果 Xi
直接 依赖 Xk
, Xk
Xj
则依赖于。Xj
depends on Xi
if Xj
depends directly on Xi
or if Xi
depends directly on Xk
and Xk
depends on Xj
. 因此 "依赖于" 是传递的,而不是 "直接依赖于" 的反身闭包。Thus "depends on" is the transitive but not reflexive closure of "depends directly on".
输出类型推断Output type inferences
从 表达式到类型的 输出类型推理 E
T
按以下方式进行:An output type inference is made from an expression E
to a type T
in the following way:
- 如果
E
是具有推断返回类型的匿名函数U
(推断出的返回类型) 并且T
是具有返回类型的委托类型或表达式树类型Tb
,则 从 到进行) 下限推理 (U
Tb
。IfE
is an anonymous function with inferred return typeU
(Inferred return type) andT
is a delegate type or expression tree type with return typeTb
, then a lower-bound inference (Lower-bound inferences) is made fromU
toTb
. - 否则,如果
E
是方法组,并且T
是具有参数类型和返回类型的委托类型或表达式树类型T1...Tk
Tb
,并且具有类型的重载解析E
T1...Tk
生成具有返回类型的单个方法U
,则将 从 到进行 下限推理U
Tb
。Otherwise, ifE
is a method group andT
is a delegate type or expression tree type with parameter typesT1...Tk
and return typeTb
, and overload resolution ofE
with the typesT1...Tk
yields a single method with return typeU
, then a lower-bound inference is made fromU
toTb
. - 否则,如果
E
是类型为的表达式U
,则将 从 到进行 下限推理U
T
。Otherwise, ifE
is an expression with typeU
, then a lower-bound inference is made fromU
toT
. - 否则,不进行推断。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
是使用参数类型的显式类型化匿名函数U1...Uk
,并且T
是具有参数类型的委托类型或表达式树类型,V1...Vk
则对于每个Ui
精确推理 (精确 推断) 从Ui
到 相应的Vi
。IfE
is an explicitly typed anonymous function with parameter typesU1...Uk
andT
is a delegate type or expression tree type with parameter typesV1...Vk
then for eachUi
an exact inference (Exact inferences) is made fromUi
to the correspondingVi
.
精确推断Exact inferences
从 类型到类型的 精确推理 如下所示 U
V
:An exact inference from a type U
to a type V
is made as follows:
如果
V
是一个未 固定 的,则Xi
U
会将添加到的一组完全限定Xi
。IfV
is one of the unfixedXi
thenU
is added to the set of exact bounds forXi
.否则,
V1...Vk
U1...Uk
通过检查是否存在以下任何情况,确定了和的设置:Otherwise, setsV1...Vk
andU1...Uk
are determined by checking if any of the following cases apply:V
是数组类型V1[...]
,并且U
是同一秩的数组类型U1[...]
V
is an array typeV1[...]
andU
is an array typeU1[...]
of the same rankV
的类型为V1?
,U
类型为U1?
V
is the typeV1?
andU
is the typeU1?
V
是构造类型C<V1...Vk>
,U
是构造类型C<U1...Uk>
V
is a constructed typeC<V1...Vk>
andU
is a constructed typeC<U1...Uk>
如果这些情况中的任何一个都适用,则会 从 每个类到相应的进行 精确推断
Ui
Vi
。If any of these cases apply then an exact inference is made from eachUi
to the correspondingVi
.否则,不进行推断。Otherwise no inferences are made.
下限推理Lower-bound inferences
从 类型到类型的 下限推理 如下所示 U
V
:A lower-bound inference from a type U
to a type V
is made as follows:
如果
V
是一个未 固定 的,则Xi
U
会将添加到的下限集Xi
。IfV
is one of the unfixedXi
thenU
is added to the set of lower bounds forXi
.否则,如果
V
为类型V1?
,并且U
为类型,U1?
则从到的位置进行下限推理U1
V1
。Otherwise, ifV
is the typeV1?
andU
is the typeU1?
then a lower bound inference is made fromU1
toV1
.否则,
U1...Uk
V1...Vk
通过检查是否存在以下任何情况,确定了和的设置:Otherwise, setsU1...Uk
andV1...Vk
are determined by checking if any of the following cases apply:V
是数组类型V1[...]
,U
是 (的数组类型,U1[...]
或是其有效基类型U1[...]
) 相同级别的类型参数V
is an array typeV1[...]
andU
is an array typeU1[...]
(or a type parameter whose effective base type isU1[...]
) of the same rankV
为IEnumerable<V1>
,或为ICollection<V1>
一IList<V1>
U
维数组类型U1[]
(或有效基类型为) 的类型参数U1[]
V
is one ofIEnumerable<V1>
,ICollection<V1>
orIList<V1>
andU
is a one-dimensional array typeU1[]
(or a type parameter whose effective base type isU1[]
)V
是一个构造的类、结构、接口或委托类型C<V1...Vk>
,并且有一个唯一C<U1...Uk>
的类型,这样U
(或者,如果U
是一个类型参数,则它的有效的基类或其有效接口集) 的任何成员完全相同、继承自 (直接或间接) ,或直接或间接 ()C<U1...Uk>
。V
is a constructed class, struct, interface or delegate typeC<V1...Vk>
and there is a unique typeC<U1...Uk>
such thatU
(or, ifU
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> {}
,当从推断到时,不会进行推理U
,C<T>
因为U1
可能是X
或Y
。 ) (The "uniqueness" restriction means that in the case interfaceC<T> {} class U: C<X>, C<Y> {}
, then no inference is made when inferring fromU
toC<T>
becauseU1
could beX
orY
.)
如果这些情况中的任何一个都适用,则将 从 每个的推理
Ui
到 对应的,如下所示Vi
:If any of these cases apply then an inference is made from eachUi
to the correspondingVi
as follows:- 如果
Ui
不知道是引用类型,则进行 精确推理IfUi
is not known to be a reference type then an exact inference is made - 否则,如果
U
是数组类型,则进行 下限推理Otherwise, ifU
is an array type then a lower-bound inference is made - 否则,如果
V
为,C<V1...Vk>
则推理依赖于的第 i 个类型参数C
:Otherwise, ifV
isC<V1...Vk>
then inference depends on the i-th type parameter ofC
:- 如果它是协变的,则进行 下限推理 。If it is covariant then a lower-bound inference is made.
- 如果它是逆变的,则进行 上限推断 。If it is contravariant then an upper-bound inference is made.
- 如果它是固定的,则进行 精确推理 。If it is invariant then an exact inference is made.
否则,不进行推断。Otherwise, no inferences are made.
上限推理Upper-bound inferences
从 类型到类型的 上限推理 如下所示 U
V
:An upper-bound inference from a type U
to a type V
is made as follows:
如果
V
是一个未 固定 的,则Xi
U
会将添加到的上限集Xi
。IfV
is one of the unfixedXi
thenU
is added to the set of upper bounds forXi
.否则,
V1...Vk
U1...Uk
通过检查是否存在以下任何情况,确定了和的设置:Otherwise, setsV1...Vk
andU1...Uk
are determined by checking if any of the following cases apply:U
是数组类型U1[...]
,并且V
是同一秩的数组类型V1[...]
U
is an array typeU1[...]
andV
is an array typeV1[...]
of the same rankU
为IEnumerable<Ue>
,或为ICollection<Ue>
一IList<Ue>
V
维数组类型Ve[]
U
is one ofIEnumerable<Ue>
,ICollection<Ue>
orIList<Ue>
andV
is a one-dimensional array typeVe[]
U
的类型为U1?
,V
类型为V1?
U
is the typeU1?
andV
is the typeV1?
U
是构造的类、结构、接口或委托类型,C<U1...Uk>
并且V
是与相同的类、结构、接口或委托类型,继承自 (直接或间接) ,或直接或间接实现 () 唯一类型C<V1...Vk>
U
is constructed class, struct, interface or delegate typeC<U1...Uk>
andV
is a class, struct, interface or delegate type which is identical to, inherits from (directly or indirectly), or implements (directly or indirectly) a unique typeC<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 haveinterface C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}
, then no inference is made when inferring fromC<U1>
toV<Q>
. 不会从或中进行推断U1
X<Q>
Y<Q>
。 ) Inferences are not made fromU1
to eitherX<Q>
orY<Q>
.)
如果这些情况中的任何一个都适用,则将 从 每个的推理
Ui
到 对应的,如下所示Vi
:If any of these cases apply then an inference is made from eachUi
to the correspondingVi
as follows:- 如果
Ui
不知道是引用类型,则进行 精确推理IfUi
is not known to be a reference type then an exact inference is made - 否则,如果
V
是数组类型,则进行 上限推理Otherwise, ifV
is an array type then an upper-bound inference is made - 否则,如果
U
为,C<U1...Uk>
则推理依赖于的第 i 个类型参数C
:Otherwise, ifU
isC<U1...Uk>
then inference depends on the i-th type parameter ofC
:- 如果它是协变的,则进行 上限推理 。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
作为的边界集中的所有类型的集合开始Xi
。The set of candidate typesUj
starts out as the set of all types in the set of bounds forXi
. - 接下来,我们依次检查每个绑定
Xi
:对于与U
Xi
Uj
U
从候选集删除的所有类型不同的每个完全绑定,都是如此。We then examine each bound forXi
in turn: For each exact boundU
ofXi
all typesUj
which are not identical toU
are removed from the candidate set. 对于U
Xi
不从其隐式转换的所有类型的每个下限,Uj
U
从候选集删除。For each lower boundU
ofXi
all typesUj
to which there is not an implicit conversion fromU
are removed from the candidate set. 对于U
不将隐式转换为的所有类型的每个上限,从Xi
Uj
U
候选集删除。For each upper boundU
ofXi
all typesUj
from which there is not an implicit conversion toU
are removed from the candidate set. - 如果其余的候选类型中
Uj
有一个唯一类型V
,其中存在到所有其他候选类型的隐式转换,则Xi
修复为V
。If among the remaining candidate typesUj
there is a unique typeV
from which there is an implicit conversion to all the other candidate types, thenXi
is fixed toV
. - 否则,类型推理将失败。Otherwise, type inference fails.
推断出的返回类型Inferred return type
在 F
类型推理和重载决策过程中,将使用推断出的匿名函数的返回类型。The inferred return type of an anonymous function F
is used during type inference and overload resolution. 只能为所有参数类型已知的匿名函数确定推理出的返回类型,这可能是因为显式给定了这些参数类型,或者是通过匿名函数转换提供的,或者是在封闭泛型方法调用的类型推理过程中推断出来的。The inferred return type can only be determined for an anonymous function where all parameter types are known, either because they are explicitly given, provided through an anonymous function conversion or inferred during type inference on an enclosing generic method invocation.
按如下方式确定 推断结果类型 :The inferred result type is determined as follows:
- 如果的主体
F
是具有类型的 表达式 ,则推断出的结果类型F
是该表达式的类型。If the body ofF
is an expression that has a type, then the inferred result type ofF
is the type of that expression. - 如果的主体
F
为 块 ,并且块的语句中的表达式集return
具有最佳通用类型T
() 中 查找一组表达式的最佳通用类型 ,则推断出的结果类型F
为T
。If the body ofF
is a block and the set of expressions in the block'sreturn
statements has a best common typeT
(Finding the best common type of a set of expressions), then the inferred result type ofF
isT
. - 否则,无法为推断结果类型
F
。Otherwise, a result type cannot be inferred forF
.
按如下方式确定 推断出的返回类型 :The inferred return type is determined as follows:
- 如果
F
是异步的,并且的主体F
是被归类为 Nothing (expression 分类) 的表达式,或者是没有 return 语句的语句块具有表达式,则推断出的返回类型为System.Threading.Tasks.Task
IfF
is async and the body ofF
is either an expression classified as nothing (Expression classifications), or a statement block where no return statements have expressions, the inferred return type isSystem.Threading.Tasks.Task
- 如果
F
为 async 并且具有推理结果类型T
,则推断出的返回类型为System.Threading.Tasks.Task<T>
。IfF
is async and has an inferred result typeT
, the inferred return type isSystem.Threading.Tasks.Task<T>
. - 如果是非
F
async 并且具有推断结果类型T
,则推断出的返回类型为T
。IfF
is non-async and has an inferred result typeT
, the inferred return type isT
. - 否则,将无法推断返回类型
F
。Otherwise a return type cannot be inferred forF
.
作为涉及匿名函数的类型推理的示例,请考虑 Select
在类中声明的扩展方法 System.Linq.Enumerable
: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
具有类型为 Name
的属性的类 string
,则 Select
可使用方法选择客户列表的名称:Assuming the System.Linq
namespace was imported with a using
clause, and given a class Customer
with a Name
property of type string
, the Select
method can be used to select the names of a list of customers:
List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);
通过将调用重写为静态方法调用来处理 (扩展方法 调用) 的扩展方法调用 Select
:The extension method invocation (Extension method invocations) of Select
is processed by rewriting the invocation to a static method invocation:
IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);
由于未显式指定类型参数,因此将使用类型推理来推断类型参数。Since type arguments were not explicitly specified, type inference is used to infer the type arguments. 首先, customers
自变量与 source
参数相关,并推断 T
为 Customer
。First, the customers
argument is related to the source
parameter, inferring T
to be Customer
. 然后,使用上面所述的匿名函数类型推理过程, c
指定类型 Customer
,并且表达式 c.Name
与参数的返回类型 selector
(推断为)相关 S
string
。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"
与参数相关 value
,推断 X
为 string
。proceeds as follows: First, the argument "1:15:30"
is related to the value
parameter, inferring X
to be string
. 然后,为第一个匿名函数的参数 s
提供推断类型 string
,且表达式 TimeSpan.Parse(s)
与的返回类型 f1
(推断为)相关 Y
System.TimeSpan
。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
提供推断类型 System.TimeSpan
,且表达式 t.TotalSeconds
与的返回类型 f2
(推断为)相关 Z
double
。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
. 因此,调用的结果为类型 double
。Thus, the result of the invocation is of type double
.
用于转换方法组的类型推理Type inference for conversion of method groups
与泛型方法的调用类似,当包含泛型方法的方法组转换为给定委托类型时,还必须应用类型推理 M
D
) (方法组转换 。Similar to calls of generic methods, type inference must also be applied when a method group M
containing a generic method is converted to a given delegate type D
(Method group conversions). 给定方法Given a method
Tr M<X1...Xn>(T1 x1 ... Tm xm)
并且要将方法组 M
分配给委托类型, D
推断类型的任务是查找类型参数, S1...Sn
使表达式:and the method group M
being assigned to the delegate type D
the task of type inference is to find type arguments S1...Sn
so that the expression:
M<S1...Sn>
与) (委托声明 是兼容的 D
。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
都被视为未 固定 的,而 从 的每个参数类型到的相应参数类型,都将进行 下限推理 Uj
D
Tj
M
。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.
更准确地说,推理是使用未 固定 的类型变量开始的 X
。More precisely, the inference starts out with an unfixed type variable X
. 然后 从 每个到进行 输出类型推断 Ei
X
。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#:
- 调用 invocation_expression 中名为的方法 (方法调用) 。Invocation of a method named in an invocation_expression (Method invocations).
- ) object_creation_expression (对象创建表达式 中名为的实例构造函数的调用。Invocation of an instance constructor named in an object_creation_expression (Object creation expressions).
- 通过 element_access (元素访问) 调用索引器访问器。Invocation of an indexer accessor through an element_access (Element access).
- 在表达式中引用的预定义运算符或用户定义的运算符 (一元运算符重载决策 和 二元运算符重载解析) 。Invocation of a predefined or user-defined operator referenced in an expression (Unary operator overload resolution and Binary operator overload resolution).
其中每个上下文都定义候选函数成员的集合和参数列表,其独特方式为,如以上所列部分中所述。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
A
当满足以下所有条件时,函数成员被称为适用于自变量列表的函数成员:A function member is said to be an applicable function member with respect to an argument list A
when all of the following are true:
- 中的每个自变量
A
都对应于函数成员声明中的一个参数(如 相应的参数中所述),并且任何与之相对应的参数均为可选参数。Each argument inA
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
,参数的参数传递模式 (例如,value、ref
或out
) 与相应参数的参数传递模式相同,并且For each argument inA
, the parameter passing mode of the argument (i.e., value,ref
, orout
) 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
- 对于
ref
或out
参数,参数的类型与对应参数的类型相同。for aref
orout
parameter, the type of the argument is identical to the type of the corresponding parameter. 毕竟,ref
或out
参数是传递的参数的别名。After all, aref
orout
parameter is an alias for the argument passed.
对于包含参数数组的函数成员,如果函数成员适用于上述规则,则表示它适用于 *normal 窗体 _。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 listA
matches the total number of parameters. 如果A
参数少于函数成员声明中的固定参数数目,则无法构造函数成员的展开形式,因此不适用。IfA
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. - 否则,如果 for 参数传递模式中的每个参数
A
都与相应参数的参数传递模式相同,则展开形式适用,并且Otherwise, the expanded form is applicable if for each argument inA
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
- 对于
ref
或out
参数,参数的类型与对应参数的类型相同。for aref
orout
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}
和两个适用的函数成员,并且 Mp
Mq
具有参数类型 {P1, P2, ..., Pn}
和 {Q1, Q2, ..., Qn}
, Mp
定义为比 if 更好的函数成员 Mq
Given an argument list A
with a set of argument expressions {E1, E2, ..., En}
and two applicable function members Mp
and Mq
with parameter types {P1, P2, ..., Pn}
and {Q1, Q2, ..., Qn}
, Mp
is defined to be a better function member than Mq
if
- 对于每个参数,从到的隐式转换
Ex
Qx
不优于从到的隐式转换Ex
Px
,并且for each argument, the implicit conversion fromEx
toQx
is not better than the implicit conversion fromEx
toPx
, and - 对于至少一个参数,从到的转换
Ex
Px
优于从到的转换Ex
Qx
。for at least one argument, the conversion fromEx
toPx
is better than the conversion fromEx
toQx
.
当执行此计算时,如果 Mp
或 Mq
适用于其展开的形式,则 Px
为,或 Qx
引用参数列表的展开形式的参数。When performing this evaluation, if Mp
or Mq
is applicable in its expanded form, then Px
or Qx
refers to a parameter in the expanded form of the parameter list.
如果参数类型序列 {P1, P2, ..., Pn}
和 {Q1, Q2, ..., Qn}
等效 (也就是说,每个都 Pi
有一个到相应) 的标识转换 Qi
,则会按顺序应用以下附加中断规则来确定更好的函数成员。In case the parameter type sequences {P1, P2, ..., Pn}
and {Q1, Q2, ..., Qn}
are equivalent (i.e. each Pi
has an identity conversion to the corresponding Qi
), the following tie-breaking rules are applied, in order, to determine the better function member.
- 如果
Mp
是一个非泛型方法并且Mq
是一个泛型方法,则Mp
比更好Mq
。IfMp
is a non-generic method andMq
is a generic method, thenMp
is better thanMq
. - 否则,如果
Mp
适用于其正常窗体并且Mq
具有params
数组,并且仅适用于其展开形式,则Mp
比更好Mq
。Otherwise, ifMp
is applicable in its normal form andMq
has aparams
array and is applicable only in its expanded form, thenMp
is better thanMq
. - 否则,如果
Mp
具有的声明参数大于Mq
,则Mp
比更好Mq
。Otherwise, ifMp
has more declared parameters thanMq
, thenMp
is better thanMq
. 如果这两个方法都具有params
数组,并且仅适用于其展开的形式,则会发生这种情况。This can occur if both methods haveparams
arrays and are applicable only in their expanded forms. - 否则,如果的所有参数
Mp
都具有相应的参数,而默认参数需要替换为中至少一个可选参数,则中的Mq
Mp
更好Mq
。Otherwise if all parameters ofMp
have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter inMq
thenMp
is better thanMq
. - 否则,如果
Mp
具有比更多的特定参数类型Mq
,则Mp
比更好Mq
。Otherwise, ifMp
has more specific parameter types thanMq
, thenMp
is better thanMq
. 让{R1, R2, ..., Rn}
并表示和的未实例化和未扩展的{S1, S2, ..., Sn}
参数类型Mp
Mq
。Let{R1, R2, ..., Rn}
and{S1, S2, ..., Sn}
represent the uninstantiated and unexpanded parameter types ofMp
andMq
.Mp``Mq
如果每个参数的Rx
不太具体,Sx
并且对于至少一个参数而言Rx
,其参数类型比更具体Sx
:Mp
's parameter types are more specific thanMq
's if, for each parameter,Rx
is not less specific thanSx
, and, for at least one parameter,Rx
is more specific thanSx
:- 类型参数不如非类型参数的特定类型。A type parameter is less specific than a non-type parameter.
- 与另一种类型自变量相比,如果至少有一个类型自变量是更具体) 的类型参数,并且没有类型参数的特定于其他中的相应类型参数,则构造的类型会以递归方式将其特定于其他构造类型 (。Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other.
- 数组类型比具有相同维数的另一个数组类型 (更为具体:如果第一个数组的元素类型比第二个元素类型更具体,则) 。An array type is more specific than another array type (with the same number of dimensions) if the element type of the first is more specific than the element type of the second.
- 否则,如果一个成员是非提升运算符,而另一个成员是提升运算符,则不提升的运算符更好。Otherwise if one member is a non-lifted operator and the other is a lifted operator, the non-lifted one is better.
- 否则,两个函数成员都不会更好。Otherwise, neither function member is better.
表达式的更好转换Better conversion from expression
给定了 C1
从表达式转换为类型的隐式转换 E
T1
,以及 C2
从表达式转换为类型的隐式转换, E
T2
C1
是比 C2
E
不完全匹配 T2
且至少包含以下其中一项的更好的转换:Given an implicit conversion C1
that converts from an expression E
to a type T1
, and an implicit conversion C2
that converts from an expression E
to a type T2
, C1
is a better conversion than C2
if E
does not exactly match T2
and at least one of the following holds:
E
完全匹配T1
(完全匹配的表达式)E
exactly matchesT1
(Exactly matching Expression)T1
比T2
(更好的转换目标 更好的转换目标)T1
is a better conversion target thanT2
(Better conversion target)
完全匹配的表达式Exactly matching Expression
给定表达式 E
和类型 T
, E
T
如果下列其中一项为,则完全匹配:Given an expression E
and a type T
, E
exactly matches T
if one of the following holds:
E
具有类型S
,并且存在从到的标识转换S``T
E
has a typeS
, and an identity conversion exists fromS
toT
E
匿名函数为T
委托类型D
或表达式目录树类型Expression<D>
以及以下保留之一:E
is an anonymous function,T
is either a delegate typeD
or an expression tree typeExpression<D>
and one of the following holds:- 推理出的返回类型
X
存在于E
D
(推导出的返回类型) 的参数列表中,并且存在从X
到的返回类型的标识转换D
An inferred return typeX
exists forE
in the context of the parameter list ofD
(Inferred return type), and an identity conversion exists fromX
to the return type ofD
- 要么
E
是非异步的,都D
具有返回类型Y
,或者是异步的,并且E
D
具有返回类型和Task<Y>
以下保留之一:EitherE
is non-async andD
has a return typeY
orE
is async andD
has a return typeTask<Y>
, and one of the following holds:- 的正文
E
是一个完全匹配的表达式Y
The body ofE
is an expression that exactly matchesY
- 的主体
E
是一个语句块,其中每个 return 语句返回一个完全匹配的表达式Y
The body ofE
is a statement block where every return statement returns an expression that exactly matchesY
- 的正文
- 推理出的返回类型
更好的转换目标Better conversion target
给定两种不同的类型 T1
和时 T2
, T1
是比 T2
从到不存在的隐式转换更好的转换目标 T2
T1
,并且至少包含以下其中一项:Given two different types T1
and T2
, T1
is a better conversion target than T2
if no implicit conversion from T2
to T1
exists, and at least one of the following holds:
- 从到的隐式转换
T1
T2
An implicit conversion fromT1
toT2
exists T1
委托类型D1
或表达式目录树类型为Expression<D1>
T2
委托类型D2
或表达式树类型Expression<D2>
,D1
具有返回类型S1
和以下保留之一:T1
is either a delegate typeD1
or an expression tree typeExpression<D1>
,T2
is either a delegate typeD2
or an expression tree typeExpression<D2>
,D1
has a return typeS1
and one of the following holds:D2
void 返回D2
is void returningD2
具有返回类型S2
,并且S1
是更好的转换目标S2
D2
has a return typeS2
, andS1
is a better conversion target thanS2
T1
为,为Task<S1>
T2
,是Task<S2>
S1
比更好的转换目标S2
T1
isTask<S1>
,T2
isTask<S2>
, andS1
is a better conversion target thanS2
T1
为S1
或,S1?
其中S1
是带符号的整数类型,而T2
是或,S2
S2?
其中S2
是无符号整数类型。T1
isS1
orS1?
whereS1
is a signed integral type, andT2
isS2
orS2?
whereS2
is an unsigned integral type. 具体而言:Specifically:S1
为sbyte
S2
,并且为byte
、、ushort
uint
或ulong
S1
issbyte
andS2
isbyte
,ushort
,uint
, orulong
S1
为short
S2
,并且为ushort
、uint
或ulong
S1
isshort
andS2
isushort
,uint
, orulong
S1
为int
S2
,为uint
,或者ulong
S1
isint
andS2
isuint
, orulong
S1
为long
,并且S2
为ulong
S1
islong
andS2
isulong
泛型类中的重载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 typedynamic
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 asthis
(This access).
函数成员调用的运行时处理包括以下步骤,其中 M
是函数成员,如果 M
是实例成员, E
则为实例表达式:The run-time processing of a function member invocation consists of the following steps, where M
is the function member and, if M
is an instance member, E
is the instance expression:
如果
M
是静态函数成员:IfM
is a static function member:- 参数列表按 参数列表中所述进行计算。The argument list is evaluated as described in Argument lists.
- 调用
M
。M
is invoked.
如果
M
是在 value_type 中声明的实例函数成员:IfM
is an instance function member declared in a value_type:E
进行计算。E
is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, then no further steps are executed.- 如果
E
未归类为变量,则将创建类型的临时本地变量,E
并将的值E
分配给该变量。IfE
is not classified as a variable, then a temporary local variable ofE
's type is created and the value ofE
is assigned to that variable.E
然后,将重新分类为对该临时本地变量的引用。E
is then reclassified as a reference to that temporary local variable. 临时变量可作为内的this
访问M
,但不能以任何其他方式进行访问。The temporary variable is accessible asthis
withinM
, but not in any other way. 因此,只有在E
为真正的变量时,调用方才能观察到所做的更改M
this
。Thus, only whenE
is a true variable is it possible for the caller to observe the changes thatM
makes tothis
. - 参数列表按 参数列表中所述进行计算。The argument list is evaluated as described in Argument lists.
- 调用
M
。M
is invoked. 引用的变量E
成为引用的变量this
。The variable referenced byE
becomes the variable referenced bythis
.
如果
M
是在 reference_type 中声明的实例函数成员:IfM
is an instance function member declared in a reference_type:E
进行计算。E
is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, then no further steps are executed.- 参数列表按 参数列表中所述进行计算。The argument list is evaluated as described in Argument lists.
- 如果的类型
E
是 value_type,则会执行装箱转换 (装箱 转换,) 将其转换E
为类型object
,并E
object
在以下步骤中将其视为类型。If the type ofE
is a value_type, a boxing conversion (Boxing conversions) is performed to convertE
to typeobject
, andE
is considered to be of typeobject
in the following steps. 在这种情况下,M
只能是的成员System.Object
。In this case,M
could only be a member ofSystem.Object
. - 的值
E
被检查为有效。The value ofE
is checked to be valid. 如果的值E
为null
,则System.NullReferenceException
会引发,而不执行进一步的步骤。If the value ofE
isnull
, aSystem.NullReferenceException
is thrown and no further steps are executed. - 确定要调用的函数成员实现:The function member implementation to invoke is determined:
- 如果的绑定时类型
E
是接口,则调用的函数成员是由M
引用的实例的运行时类型提供的实现E
。If the binding-time type ofE
is an interface, the function member to invoke is the implementation ofM
provided by the run-time type of the instance referenced byE
. 此函数成员是通过以下方式确定的:将接口映射规则应用 (接口映射) ,以确定由M
引用的实例的运行时类型提供的实现E
。This function member is determined by applying the interface mapping rules (Interface mapping) to determine the implementation ofM
provided by the run-time type of the instance referenced byE
. - 否则,如果
M
是虚函数成员,则调用的函数成员是由M
引用的实例的运行时类型提供的实现E
。Otherwise, ifM
is a virtual function member, the function member to invoke is the implementation ofM
provided by the run-time type of the instance referenced byE
. 此函数成员是通过应用用于确定派生程度最高的实现 (虚拟方法的规则来确定的,) 的 虚拟方法M
与所引用的实例的运行时类型有关E
。This function member is determined by applying the rules for determining the most derived implementation (Virtual methods) ofM
with respect to the run-time type of the instance referenced byE
. - 否则,
M
为非虚拟函数成员,要调用的函数成员本身就是M
。Otherwise,M
is a non-virtual function member, and the function member to invoke isM
itself.
- 如果的绑定时类型
- 调用上述步骤中确定的函数成员实现。The function member implementation determined in the step above is invoked. 引用的对象
E
成为引用的对象this
。The object referenced byE
becomes the object referenced bythis
.
在装箱实例上调用Invocations on boxed instances
在 value_type 中实现的函数成员可在以下情况下通过 value_type 的装箱实例进行调用:A function member implemented in a value_type can be invoked through a boxed instance of that value_type in the following situations:
- 如果函数成员是
override
从类型继承的方法的object
,则通过类型的实例表达式调用object
。When the function member is anoverride
of a method inherited from typeobject
and is invoked through an instance expression of typeobject
. - 当函数成员是接口函数成员的实现并通过 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_expression s 和 primary_no_array_creation_expression 之间。Primary expressions are divided between array_creation_expression s and primary_no_array_creation_expression s. 以这种方式对待数组创建表达式,而不是将其与其他简单的表达式窗体一起列出,而是允许语法禁止可能的代码混乱,如Treating array-creation-expression in this way, rather than listing it along with the other simple expression forms, enables the grammar to disallow potentially confusing code such as
object o = new int[3][1];
否则,会将其解释为which would otherwise be interpreted as
object o = (new int[3])[1];
文本Literals
由 文本 (文本) 组成的 primary_expression 分类为值。A primary_expression that consists of a literal (Literals) is classified as a value.
内插字符串Interpolated strings
Interpolated_string_expression 包含一个符号, $
后面跟一个常规或原义字符串文本,其中的洞由和分隔,其中 {
}
包含表达式和格式规范。An interpolated_string_expression consists of a $
sign followed by a regular or verbatim string literal, wherein holes, delimited by {
and }
, enclose expressions and formatting specifications. 内插字符串表达式是已分解为单独标记的 interpolated_string_literal 的结果,如内 插字符串文本中所述。An interpolated string expression is the result of an interpolated_string_literal that has been broken up into individual tokens, as described in Interpolated string literals.
interpolated_string_expression
: '$' interpolated_regular_string
| '$' interpolated_verbatim_string
;
interpolated_regular_string
: interpolated_regular_string_whole
| interpolated_regular_string_start interpolated_regular_string_body interpolated_regular_string_end
;
interpolated_regular_string_body
: interpolation (interpolated_regular_string_mid interpolation)*
;
interpolation
: expression
| expression ',' constant_expression
;
interpolated_verbatim_string
: interpolated_verbatim_string_whole
| interpolated_verbatim_string_start interpolated_verbatim_string_body interpolated_verbatim_string_end
;
interpolated_verbatim_string_body
: interpolation (interpolated_verbatim_string_mid interpolation)+
;
内插中的 constant_expression 必须将隐式转换为 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. 否则,它的类型为 string
。Otherwise, it has the type string
.
如果内插字符串的类型为 System.IFormattable
或 System.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.
格式字符串的构造如下所示,其中 N
是 interpolated_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_whole 或 interpolated_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_start 或 interpolated_verbatim_string_startFirst the interpolated_regular_string_start or interpolated_verbatim_string_start
- 然后,将每个数字
I
从0
到N-1
:Then for each numberI
from0
toN-1
:- 的十进制表示形式
I
The decimal representation ofI
- 然后,如果相应的 内插 具有 constant_expression,则
,
(逗号) 后跟的值的十进制表示形式 constant_expressionThen, 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_mid、 interpolated_regular_string_end、 interpolated_verbatim_string_mid 或 interpolated_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>
,其中 I
是单个标识符, <A1,...,Ak>
是可选 type_argument_list。A simple_name is either of the form I
or of the form I<A1,...,Ak>
, where I
is a single identifier and <A1,...,Ak>
is an optional type_argument_list. 当未指定 type_argument_list 时,请考虑 K
为零。When no type_argument_list is specified, consider K
to be zero. 将按如下所示对 simple_name 进行评估和分类:The simple_name is evaluated and classified as follows:
如果
K
为零,并且 simple_name 出现在 块 中,并且 块 的 (或封闭 块) 局部变量声明空间 (声明 中) 包含名称为的局部变量、参数或常量I
,则 simple_name 引用该局部变量、参数或常量,并归类为变量或值。IfK
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 nameI
, then the simple_name refers to that local variable, parameter or constant and is classified as a variable or value.如果
K
为零,并且 simple_name 出现在泛型方法声明的主体中,并且如果声明包含名称为的类型参数I
,则 simple_name 引用该类型参数。IfK
is zero and the simple_name appears within the body of a generic method declaration and if that declaration includes a type parameter with nameI
, then the simple_name refers to that type parameter.否则,对于每个实例类型
T
(实例类型) ,从立即封闭类型声明的实例类型开始,并继续执行每个封闭类或结构声明的实例类型(如果有任何) ) (:Otherwise, for each instance typeT
(The instance type), starting with the instance type of the immediately enclosing type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):- 如果
K
为零,的声明T
包含名为的类型参数I
,则 simple_name 引用该类型参数。IfK
is zero and the declaration ofT
includes a type parameter with nameI
, then the simple_name refers to that type parameter. - 否则,如果中的成员查找 (成员查找)
I
中T
的K
类型自变量产生匹配:Otherwise, if a member lookup (Member lookup) ofI
inT
withK
type arguments produces a match:- 如果
T
是直接封闭类或结构类型的实例类型,并且查找标识了一个或多个方法,则结果是具有关联的实例表达式的方法组this
。IfT
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 ofthis
. 如果指定了类型参数列表,它将用于调用泛型方法 (方法调用) 。If a type argument list was specified, it is used in calling a generic method (Method invocations). - 否则,如果
T
是直接封闭类或结构类型的实例类型,如果查找标识实例成员并且引用出现在实例构造函数、实例方法或实例访问器的主体中,则结果与窗体 (成员访问) 的成员访问相同this.I
。Otherwise, ifT
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 formthis.I
. 仅当为零时才会发生这种情况K
。This can only happen whenK
is zero. - 否则,结果与窗体或 (成员访问) 的成员访问相同
T.I
T.I<A1,...,Ak>
。Otherwise, the result is the same as a member access (Member access) of the formT.I
orT.I<A1,...,Ak>
. 在这种情况下, simple_name 引用实例成员,则为绑定时错误。In this case, it is a binding-time error for the simple_name to refer to an instance member.
- 如果
- 如果
否则,对于每个命名空间
N
,从 simple_name 发生的命名空间开始,继续每个封闭命名空间 (如果任何) ,并在全局命名空间结束时,将计算以下步骤,直到找到实体:Otherwise, for each namespaceN
, starting with the namespace in which the simple_name occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:- 如果
K
为零,并且I
是中的命名空间的名称N
,则:IfK
is zero andI
is the name of a namespace inN
, then:- 如果 simple_name 发生的位置由的命名空间声明括起来
N
,并且命名空间声明包含将名称与命名空间或类型相关联的 extern_alias_directive 或 using_alias_directiveI
,则 simple_name 是不明确的,并发生编译时错误。If the location where the simple_name occurs is enclosed by a namespace declaration forN
and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs. - 否则, simple_name 引用中名为的命名
I
空间N
。Otherwise, the simple_name refers to the namespace namedI
inN
.
- 如果 simple_name 发生的位置由的命名空间声明括起来
- 否则,如果
N
包含具有 name 和 type 参数的可访问类型I
K
,则:Otherwise, ifN
contains an accessible type having nameI
andK
type parameters, then:- 如果
K
为零,并且 simple_name 的位置由的命名空间声明括起来,N
并且命名空间声明包含将名称与命名空间或类型相关联的 extern_alias_directive 或 using_alias_directiveI
,则 simple_name 是不明确的,并发生编译时错误。IfK
is zero and the location where the simple_name occurs is enclosed by a namespace declaration forN
and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
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 发生的位置由的命名空间声明括起来,则
N
:Otherwise, if the location where the simple_name occurs is enclosed by a namespace declaration forN
:- 如果
K
为零,并且命名空间声明包含将名称与导入的命名空间或类型相关联的 extern_alias_directive 或 using_alias_directiveI
,则 simple_name 引用该命名空间或类型。IfK
is zero and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the nameI
with an imported namespace or type, then the simple_name refers to that namespace or type. - 否则,如果 using_namespace_directive s 和 using_static_directive s 的命名空间和类型声明都包含一个具有 name 和 type 参数的可访问类型或非扩展静态成员
I
K
,则 simple_name 引用使用给定类型参数构造的类型或成员。Otherwise, if the namespaces and type declarations imported by the using_namespace_directive s and using_static_directive s of the namespace declaration contain exactly one accessible type or non-extension static member having nameI
andK
type parameters, then the simple_name refers to that type or member constructed with the given type arguments. - 否则,如果命名空间声明中的 using_namespace_directive s 所导入的命名空间和类型包含多个具有名称和类型参数的可访问类型或非扩展方法静态成员
I
K
,则 simple_name 不明确,并发生错误。Otherwise, if the namespaces and types imported by the using_namespace_directive s of the namespace declaration contain more than one accessible type or non-extension-method static member having nameI
andK
type parameters, then the simple_name is ambiguous and an error occurs.
- 如果
请注意,此完整步骤与处理 namespace_or_type_name (命名空间和类型名称) 的相应步骤完全相同。Note that this entire step is exactly parallel to the corresponding step in the processing of a namespace_or_type_name (Namespace and type names).
- 如果
否则, simple_name 是未定义的,并且发生编译时错误。Otherwise, the simple_name is undefined and a compile-time error occurs.
带括号的表达式Parenthesized expressions
Parenthesized_expression 包含用括号括起来的 表达式。A parenthesized_expression consists of an expression enclosed in parentheses.
parenthesized_expression
: '(' expression ')'
;
通过计算括号内的 表达式 来计算 parenthesized_expression 。A parenthesized_expression is evaluated by evaluating the expression within the parentheses. 如果括号内的 表达式 表示命名空间或类型,则会发生编译时错误。If the expression within the parentheses denotes a namespace or type, a compile-time error occurs. 否则, parenthesized_expression 的结果是包含的 表达式 的计算结果。Otherwise, the result of the parenthesized_expression is the result of the evaluation of the contained expression.
成员访问Member access
Member_access 包含 primary_expression、 predefined_type 或 qualified_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>
,其中 E
是主表达式,是 I
单个标识符, <A1, ..., Ak>
是可选 type_argument_list。A member_access is either of the form E.I
or of the form E.I<A1, ..., Ak>
, where E
is a primary-expression, I
is a single identifier and <A1, ..., Ak>
is an optional type_argument_list. 当未指定 type_argument_list 时,请考虑 K
为零。When no type_argument_list is specified, consider K
to be zero.
Primary_expression 类型的 member_access dynamic
(动态绑定) 动态绑定。A member_access with a primary_expression of type dynamic
is dynamically bound (Dynamic binding). 在这种情况下,编译器将成员访问归类为类型的属性访问 dynamic
。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_expression 的 primary_expression 。If this run-time classification leads to a method group, then the member access must be the primary_expression of an invocation_expression.
将按如下所示对 member_access 进行评估和分类:The member_access is evaluated and classified as follows:
- 如果
K
为零,并且E
是一个命名空间,并且E
包含名为的嵌套命名空间I
,则结果为该命名空间。IfK
is zero andE
is a namespace andE
contains a nested namespace with nameI
, then the result is that namespace. - 否则,如果
E
是一个命名空间,并且E
包含具有 name 和 type 参数的可访问类型I
K
,则结果是使用给定类型参数构造的类型。Otherwise, ifE
is a namespace andE
contains an accessible type having nameI
andK
type parameters, then the result is that type constructed with the given type arguments. - 如果
E
是 predefined_type 或归类为类型 primary_expression ,则如果不是E
类型形参,并且具有类型形参的中的成员查找 (成员查找)I
E
K
产生匹配,则按E.I
如下方式计算和分类:IfE
is a predefined_type or a primary_expression classified as a type, ifE
is not a type parameter, and if a member lookup (Member lookup) ofI
inE
withK
type parameters produces a match, thenE.I
is evaluated and classified as follows:- 如果
I
标识一个类型,则结果为使用给定类型参数构造的类型。IfI
identifies a type, then the result is that type constructed with the given type arguments. - 如果
I
标识一个或多个方法,则结果是没有关联的实例表达式的方法组。IfI
identifies one or more methods, then the result is a method group with no associated instance expression. 如果指定了类型参数列表,它将用于调用泛型方法 (方法调用) 。If a type argument list was specified, it is used in calling a generic method (Method invocations). - 如果
I
标识static
属性,则结果是没有关联实例表达式的属性访问。IfI
identifies astatic
property, then the result is a property access with no associated instance expression. - 如果
I
标识static
字段:IfI
identifies astatic
field:- 如果字段为
readonly
并且引用出现在声明该字段的类或结构的静态构造函数之外,则结果为值,即中静态字段的值I
E
。If the field isreadonly
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 fieldI
inE
. - 否则,结果为变量,即中的静态字段
I
E
。Otherwise, the result is a variable, namely the static fieldI
inE
.
- 如果字段为
- 如果
I
标识static
事件:IfI
identifies astatic
event:- 如果引用出现在声明事件的类或结构中,并且声明事件时没有 event_accessor_declarations (事件) ,则
E.I
将像处理静态字段一样处理事件I
。If the reference occurs within the class or struct in which the event is declared, and the event was declared without event_accessor_declarations (Events), thenE.I
is processed exactly as ifI
were a static field. - 否则,结果是没有关联的实例表达式的事件访问。Otherwise, the result is an event access with no associated instance expression.
- 如果引用出现在声明事件的类或结构中,并且声明事件时没有 event_accessor_declarations (事件) ,则
- 如果
I
标识一个常数,则结果为一个值,即该常数的值。IfI
identifies a constant, then the result is a value, namely the value of that constant. - 如果
I
标识枚举成员,则结果为值,即枚举成员的值。IfI
identifies an enumeration member, then the result is a value, namely the value of that enumeration member. - 否则,
E.I
是无效的成员引用,并发生编译时错误。Otherwise,E.I
is an invalid member reference, and a compile-time error occurs.
- 如果
- 如果
E
是属性访问、索引器访问、变量或值,其中的类型为T
,并且具有类型参数的成员查找 (成员查找)I
T
K
生成匹配项,则按E.I
如下方式计算和分类:IfE
is a property access, indexer access, variable, or value, the type of which isT
, and a member lookup (Member lookup) ofI
inT
withK
type arguments produces a match, thenE.I
is evaluated and classified as follows:- 首先,如果
E
是属性或索引器访问,则 () 的 表达式值 获取属性或索引器访问的值,并将其重新分类E
为值。First, ifE
is a property or indexer access, then the value of the property or indexer access is obtained (Values of expressions) andE
is reclassified as a value. - 如果
I
标识一个或多个方法,则结果为具有的关联实例表达式的方法组E
。IfI
identifies one or more methods, then the result is a method group with an associated instance expression ofE
. 如果指定了类型参数列表,它将用于调用泛型方法 (方法调用) 。If a type argument list was specified, it is used in calling a generic method (Method invocations). - 如果
I
标识实例属性,IfI
identifies an instance property,- 如果
E
为this
,则I
标识自动实现的属性 (自动实现) 不使用 setter 的属性,并且引用出现在类或结构类型的实例构造函数中T
,则结果是一个变量,即由给定的I
实例中给定的自动属性的隐藏支持字段T
this
。IfE
isthis
,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 typeT
, then the result is a variable, namely the hidden backing field for the auto-property given byI
in the instance ofT
given bythis
. - 否则,结果为具有的关联实例表达式的属性访问
E
。Otherwise, the result is a property access with an associated instance expression ofE
.
- 如果
- 如果
T
是 class_type 并且I
标识 class_type 的实例字段:IfT
is a class_type andI
identifies an instance field of that class_type:- 如果的值
E
为null
,则System.NullReferenceException
引发。If the value ofE
isnull
, then aSystem.NullReferenceException
is thrown. - 否则,如果字段为
readonly
并且引用出现在声明该字段的类的实例构造函数之外,则结果为值,即引用的对象中的字段的值I
E
。Otherwise, if the field isreadonly
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 fieldI
in the object referenced byE
. - 否则,结果为变量,即
I
引用的对象中的字段E
。Otherwise, the result is a variable, namely the fieldI
in the object referenced byE
.
- 如果的值
- 如果
T
是 struct_type 并且I
标识 struct_type 的实例字段:IfT
is a struct_type andI
identifies an instance field of that struct_type:- 如果
E
是值,或者如果字段是readonly
,并且引用出现在声明该字段的结构的实例构造函数之外,则结果为值,即I
给定的结构实例中字段的值E
。IfE
is a value, or if the field isreadonly
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 fieldI
in the struct instance given byE
. - 否则,结果为变量,即
I
给定的结构实例中的字段E
。Otherwise, the result is a variable, namely the fieldI
in the struct instance given byE
.
- 如果
- 如果
I
标识实例事件:IfI
identifies an instance event:- 如果引用出现在声明事件的类或结构中,并且声明事件时没有 event_accessor_declarations (事件) ,并且引用不是作为或运算符的左侧出现
+=
,则-=
E.I
会完全像处理I
实例字段一样处理。If the reference occurs within the class or struct in which the event is declared, and the event was declared without event_accessor_declarations (Events), and the reference does not occur as the left-hand side of a+=
or-=
operator, thenE.I
is processed exactly as ifI
was an instance field. - 否则,结果是具有关联的实例表达式的事件访问
E
。Otherwise, the result is an event access with an associated instance expression ofE
.
- 如果引用出现在声明事件的类或结构中,并且声明事件时没有 event_accessor_declarations (事件) ,并且引用不是作为或运算符的左侧出现
- 首先,如果
- 否则,将尝试处理
E.I
为扩展方法调用 (扩展方法 调用) 。Otherwise, an attempt is made to processE.I
as an extension method invocation (Extension method invocations). 如果此操作失败,E.I
则为无效的成员引用,并且发生绑定时错误。If this fails,E.I
is an invalid member reference, and a binding-time error occurs.
相同的简单名称和类型名称Identical simple names and type names
在窗体的成员访问中 E.I
,如果 E
是单个标识符,并且 E
作为 simple_name (简单名称) 是一个常数、字段、属性、局部变量或参数,其类型与 E
作为 type_name (命名空间和类型名称) 的含义相同,则这两个可能的含义 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. 的两个可能含义 E.I
不明确,因为 I
E
在这两种情况下都必须是类型的成员。The two possible meanings of E.I
are never ambiguous, since I
must necessarily be a member of the type E
in both cases. 换言之,该规则只允许访问 E
编译时错误发生的静态成员和嵌套类型。In other words, the rule simply permits access to the static members and nested types of E
where a compile-time error would otherwise have occurred. 例如:For example:
struct Color
{
public static readonly Color White = new Color(...);
public static readonly Color Black = new Color(...);
public Color Complement() {...}
}
class A
{
public Color Color; // Field Color of type Color
void F() {
Color = Color.Black; // References Color.Black static member
Color = Color.Complement(); // Invokes Complement() on Color field
}
static void G() {
Color c = Color.White; // References Color.White static member
}
}
语法多义性Grammar ambiguities
Simple_name 的生产 (简单名称) 和 Member_access (成员访问) 可以在表达式的语法中带来歧义。The productions for simple_name (Simple names) and member_access (Member access) can give rise to ambiguities in the grammar for expressions. 例如,语句:For example, the statement:
F(G<A,B>(7));
可以解释为对 F
具有两个参数和的的 G < A
调用 B > (7)
。could be interpreted as a call to F
with two arguments, G < A
and B > (7)
. 另外,它也可以解释为 F
使用一个自变量进行调用,该参数是对 G
具有两个类型参数和一个正则参数的泛型方法的调用。Alternatively, it could be interpreted as a call to F
with one argument, which is a call to a generic method G
with two type arguments and one regular argument.
如果可以将标记序列 (在上下文中) simple_name (简单名称) 、 member_access (成员访问 ) 或 pointer_member_access () type_argument_list 类型参数 (的 指针成员访问) ,则将检查紧跟结束标记的标记 >
。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_name 的一部分保留, member_access 或 pointer_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_name、 member_access 或 pointer_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);
每个都将解释为对 F
具有两个参数的的调用。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 后跟二元加运算符的 simple_name 。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_list 的 namespace_or_type_name 。the 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 具有编译时类型
dynamic
。The primary_expression has compile-time typedynamic
. - 至少一个可选 argument_list 的参数具有编译时类型
dynamic
,而 primary_expression 没有委托类型。At least one argument of the optional argument_list has compile-time typedynamic
and the primary_expression does not have a delegate type.
在这种情况下,编译器将 invocation_expression 分类为类型的值 dynamic
。In this case the compiler classifies the invocation_expression as a value of type dynamic
. 下面的规则将使用运行时类型(而不是具有编译时类型的 primary_expression 和参数的编译时类型)在运行时应用 invocation_expression 的含义 dynamic
。The 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_expression 的 primary_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_expression 是 delegate_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 returnsvoid
, the result is nothing. 只允许在 statement_expression (expression 语句 的上下文中使用或将表达式归类为 nothing,) 为 lambda_expression (匿名函数表达式 的主体。An expression that is classified as nothing is permitted only in the context of a statement_expression (Expression statements) or as the body of a lambda_expression (Anonymous function expressions). 否则,会发生绑定时错误。Otherwise a binding-time error occurs. - 否则,结果为方法或委托返回的类型的值。Otherwise, the result is a value of the type returned by the method or delegate.
方法调用Method invocations
对于方法调用, invocation_expression 的 primary_expression 必须为方法组。For a method invocation, the primary_expression of the invocation_expression must be a method group. 方法组标识一种要调用的方法或用于从中选择要调用的特定方法的重载方法集。The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke. 在后一种情况下,确定要调用的特定方法的方式取决于 argument_list 中的参数类型所提供的上下文。In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the argument_list.
对窗体的方法调用的绑定时处理 M(A)
,其中 M
是一个方法组 (可能包括 type_argument_list) ,并且 A
是可选的 argument_list,包括以下步骤:The binding-time processing of a method invocation of the form M(A)
, where M
is a method group (possibly including a type_argument_list), and A
is an optional argument_list, consists of the following steps:
- 构造方法调用的候选方法集。The set of candidate methods for the method invocation is constructed. 对于
F
与方法组关联的每个方法M
:For each methodF
associated with the method groupM
:- 如果
F
为非泛型,则在以下情况下F
是候选项:IfF
is non-generic,F
is a candidate when:M
没有类型参数列表,并且M
has no type argument list, andF
适用于A
(适用的函数成员) 。F
is applicable with respect toA
(Applicable function member).
- 如果
F
是泛型并且没有M
类型参数列表,则在以下情况下F
是候选项:IfF
is generic andM
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 ofF
is applicable with respect toA
(Applicable function member).
- 如果
F
为泛型并且M
包含类型参数列表,则在以下情况下F
是候选项:IfF
is generic andM
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 ofF
is applicable with respect toA
(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 methodC.F
in the set, whereC
is the type in which the methodF
is declared, all methods declared in a base type ofC
are removed from the set. 此外,如果C
是以外的类类型,则将object
从集合中删除在接口类型中声明的所有方法。Furthermore, ifC
is a class type other thanobject
, 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_name 或 member_access 。The method is validated in the context of the method group: If the best method is a static method, the method group must have resulted from a simple_name or a member_access through a type. 如果最佳方法是实例方法,则方法组必须由 simple_name、 member_access 通过变量或值或 base_access 导致。If the best method is an instance method, the method group must have resulted from a simple_name, a member_access through a variable or value, or a base_access. 如果这两个要求都不成立,则发生绑定时错误。If neither of these requirements is true, a binding-time error occurs.
- 如果最佳方法是泛型方法,则会对照约束来检查 (提供或推断) 的类型参数, (满足 泛型方法上声明) 的约束。If the best method is a generic method, the type arguments (supplied or inferred) are checked against the constraints (Satisfying constraints) declared on the generic method. 如果任何类型自变量不满足类型参数上的) (,则会发生绑定时错误。If any type argument does not satisfy the corresponding constraint(s) on the type parameter, a binding-time error occurs.
按照上述步骤在绑定时选择并验证方法后,将根据 动态重载解析的编译时检查中所述的函数成员调用规则来处理实际的运行时调用。Once a method has been selected and validated at binding-time by the above steps, the actual run-time invocation is processed according to the rules of function member invocation described in Compile-time checking of dynamic overload resolution.
上述解决方法的直观效果如下所示:若要查找由方法调用调用的特定方法,请从方法调用指示的类型开始,然后继续继承链,直到至少找到一个适用的、可访问的、非重写方法声明。The intuitive effect of the resolution rules described above is as follows: To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. 然后,对该类型中声明的适用的、可访问的、非重写方法集执行类型推理和重载决策,并调用此方法。Then perform type inference and overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected. 如果未找到方法,请改为尝试将调用作为扩展方法调用来处理。If no method was found, try instead to process the invocation as an extension method invocation.
扩展方法调用Extension method invocations
在方法调用中 (在) 一种形式的装箱实例上调用In a method invocation (Invocations on boxed instances) of one of the forms
expr . identifier ( )
expr . identifier ( args )
expr . identifier < typeargs > ( )
expr . identifier < typeargs > ( args )
如果调用的正常处理找不到适用的方法,则会尝试将构造作为扩展方法调用来处理。if the normal processing of the invocation finds no applicable methods, an attempt is made to process the construct as an extension method invocation. 如果 expr 或任意 参数 具有编译时类型 dynamic
,则扩展方法将不适用。If expr or any of the args has compile-time type dynamic
, extension methods will not apply.
目标是查找最佳 type_name C
,以便可以进行相应的静态方法调用:The objective is to find the best type_name C
, so that the corresponding static method invocation can take place:
C . identifier ( expr )
C . identifier ( expr , args )
C . identifier < typeargs > ( expr )
C . identifier < typeargs > ( expr , args )
扩展方法 Ci.Mj
符合条件 :An extension method Ci.Mj
is eligible if:
Ci
是非泛型非泛型类Ci
is a non-generic, non-nested class- 的名称
Mj
是 标识符The name ofMj
is identifier Mj
作为静态方法应用于参数时,可访问且适用,如上所述Mj
is accessible and applicable when applied to the arguments as a static method as shown above- 存在从 expr 到的第一个参数类型的隐式标识、引用或装箱转换
Mj
。An implicit identity, reference or boxing conversion exists from expr to the type of the first parameter ofMj
.
的搜索 C
过程如下所示: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 declarationsCi
with eligible extension methodsMj
, then the set of those extension methods is the candidate set. - 如果
Ci
由给定命名空间或编译单元中 using_namespace_directive s 导入的 using_static_declarations 和直接声明的类型包含符合条件的扩展方法Mj
,则这些扩展方法集是候选集。If typesCi
imported by using_static_declarations and directly declared in namespaces imported by using_namespace_directive s in the given namespace or compilation unit directly contain eligible extension methodsMj
, then the set of those extension methods is the candidate set.
- 如果给定的命名空间或编译单元直接包含具有符合条件的扩展方法的非泛型类型声明
- 如果在任何封闭命名空间声明或编译单元中均未找到候选集,则会发生编译时错误。If no candidate set is found in any enclosing namespace declaration or compilation unit, a compile-time error occurs.
- 否则,将重载决策应用于候选集,如 (重载决策) 中所述。Otherwise, overload resolution is applied to the candidate set as described in (Overload resolution). 如果未找到单个最佳方法,则会发生编译时错误。If no single best method is found, a compile-time error occurs.
C
是在其中将最佳方法声明为扩展方法的类型。C
is the type within which the best method is declared as an extension method.
使用 C
作为目标,将方法调用作为静态方法调用处理 () 动态重载解析的编译时检查 。Using C
as a target, the method call is then processed as a static method invocation (Compile-time checking of dynamic overload resolution).
前面的规则意味着实例方法优先于扩展方法,内部命名空间声明中提供的扩展方法优先于外部命名空间声明中提供的扩展方法,并且直接在命名空间中声明的扩展方法优先于通过使用命名空间指令导入到同一命名空间中的扩展方法。The preceding rules mean that instance methods take precedence over extension methods, that extension methods available in inner namespace declarations take precedence over extension methods available in outer namespace declarations, and that extension methods declared directly in a namespace take precedence over extension methods imported into that same namespace with a using namespace directive. 例如:For example:
public static class E
{
public static void F(this object obj, int i) { }
public static void F(this object obj, string s) { }
}
class A { }
class B
{
public void F(int i) { }
}
class C
{
public void F(object obj) { }
}
class X
{
static void Test(A a, B b, C c) {
a.F(1); // E.F(object, int)
a.F("hello"); // E.F(object, string)
b.F(1); // B.F(int)
b.F("hello"); // E.F(object, string)
c.F(1); // C.F(object)
c.F("hello"); // C.F(object)
}
}
在此示例中, B
的方法优先于第一个扩展方法,并且 C
的方法优先于这两个扩展方法。In the example, B
's method takes precedence over the first extension method, and C
's method takes precedence over both extension methods.
public static class C
{
public static void F(this int i) { Console.WriteLine("C.F({0})", i); }
public static void G(this int i) { Console.WriteLine("C.G({0})", i); }
public static void H(this int i) { Console.WriteLine("C.H({0})", i); }
}
namespace N1
{
public static class D
{
public static void F(this int i) { Console.WriteLine("D.F({0})", i); }
public static void G(this int i) { Console.WriteLine("D.G({0})", i); }
}
}
namespace N2
{
using N1;
public static class E
{
public static void F(this int i) { Console.WriteLine("E.F({0})", i); }
}
class Test
{
static void Main(string[] args)
{
1.F();
2.G();
3.H();
}
}
}
此示例的输出为:The output of this example is:
E.F(1)
D.G(2)
C.H(3)
D.G
优先于 C.G
,并且 E.F
优先于 D.F
和 C.F
。D.G
takes precedence over C.G
, and E.F
takes precedence over both D.F
and C.F
.
委托调用Delegate invocations
对于委托调用, invocation_expression 的 primary_expression 必须是 delegate_type 的值。For a delegate invocation, the primary_expression of the invocation_expression must be a value of a delegate_type. 此外,将 delegate_type 视为与 delegate_type 具有相同参数列表的函数成员, delegate_type 必须 (适用的函数成员,) argument_list 的 invocation_expression。 Furthermore, considering the delegate_type to be a function member with the same parameter list as the delegate_type, the delegate_type must be applicable (Applicable function member) with respect to the argument_list of the invocation_expression.
对窗体的委托调用的运行时处理 D(A)
,其中 D
是 delegate_type 的 primary_expression , A
是可选的 argument_list,包括以下步骤:The run-time processing of a delegate invocation of the form D(A)
, where D
is a primary_expression of a delegate_type and A
is an optional argument_list, consists of the following steps:
D
进行计算。D
is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, no further steps are executed.- 的值
D
被检查为有效。The value ofD
is checked to be valid. 如果的值D
为null
,则System.NullReferenceException
会引发,而不执行进一步的步骤。If the value ofD
isnull
, aSystem.NullReferenceException
is thrown and no further steps are executed. - 否则,
D
是对委托实例的引用。Otherwise,D
is a reference to a delegate instance. 函数成员调用 (对 动态重载决策的编译时检查 ,) 在委托调用列表中的每个可调用实体上执行。Function member invocations (Compile-time checking of dynamic overload resolution) are performed on each of the callable entities in the invocation list of the delegate. 对于包含实例和实例方法的可调用实体,调用实例是包含在可调用实体中的实例。For callable entities consisting of an instance and instance method, the instance for the invocation is the instance contained in the callable entity.
元素访问Element access
Element_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 argument s, separated by commas.
element_access
: primary_no_array_creation_expression '[' expression_list ']'
;
不允许 element_access 的 argument_list 包含 ref
或 out
参数。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 具有编译时类型
dynamic
。The primary_no_array_creation_expression has compile-time typedynamic
. - Argument_list 至少有一个表达式的编译时类型为
dynamic
, primary_no_array_creation_expression 没有数组类型。At least one expression of the argument_list has compile-time typedynamic
and the primary_no_array_creation_expression does not have an array type.
在这种情况下,编译器将 element_access 分类为类型的值 dynamic
。In this case the compiler classifies the element_access as a value of type dynamic
. 下面的规则用于确定 element_access 的含义,然后使用运行时类型(而不是具有编译时类型的 primary_no_array_creation_expression 和 argument_list 表达式的编译时类型)在运行时应用 dynamic
。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_access 的 primary_no_array_creation_expression 是 array_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_access 的 primary_no_array_creation_expression 必须是 array_type 的值。For an array access, the primary_no_array_creation_expression of the element_access must be a value of an array_type. 而且,不允许数组访问的 argument_list 包含命名参数。 Argument_list 中的表达式的数目必须与 array_type 的排名相同,并且每个表达式的类型必须为、、、或,才能 int
uint
long
ulong
隐式转换为这些类型中的一个或多个。Furthermore, the argument_list of an array access is not allowed to contain named arguments.The number of expressions in the argument_list must be the same as the rank of the array_type, and each expression must be of type int
, uint
, long
, ulong
, or must be implicitly convertible to one or more of these types.
计算数组访问的结果是数组的元素类型的一个变量,即,值所选择的数组元素 (s) 的表达式 (s) 在 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.
运行时处理窗体的数组访问 P[A]
,其中 P
是 array_type 的 primary_no_array_creation_expression ,而 A
是 argument_list,其中包含以下步骤:The run-time processing of an array access of the form P[A]
, where P
is a primary_no_array_creation_expression of an array_type and A
is an argument_list, consists of the following steps:
P
进行计算。P
is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, no further steps are executed.- 将按从左到右的顺序计算 argument_list 的索引表达式。The index expressions of the argument_list are evaluated in order, from left to right. 按照每个索引表达式的计算,将执行隐式转换 (隐式 转换为以下类型之一) :
int
、uint
、long
、ulong
。Following 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
int
short
long
。For instance, if the index expression is of typeshort
then an implicit conversion toint
is performed, since implicit conversions fromshort
toint
and fromshort
tolong
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 ofP
is checked to be valid. 如果的值P
为null
,则System.NullReferenceException
会引发,而不执行进一步的步骤。If the value ofP
isnull
, aSystem.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 byP
. 如果一个或多个值超出范围,System.IndexOutOfRangeException
则会引发,而不执行进一步的步骤。If one or more values are out of range, aSystem.IndexOutOfRangeException
is thrown and no further steps are executed. - 计算 (s) 指定的数组元素位置,此位置成为数组访问的结果。The location of the array element given by the index expression(s) is computed, and this location becomes the result of the array access.
索引器访问Indexer access
对于索引器访问, element_access 的 primary_no_array_creation_expression 必须是类、结构或接口类型的变量或值,并且此类型必须实现适用于 element_access 的 argument_list 的一个或多个索引器。For an indexer access, the primary_no_array_creation_expression of the element_access must be a variable or value of a class, struct, or interface type, and this type must implement one or more indexers that are applicable with respect to the argument_list of the element_access.
对窗体的索引器访问的绑定时处理 P[A]
,其中 P
是类、结构或接口类型的 primary_no_array_creation_expression T
,而 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 byT
is constructed. 该集包含在中声明的所有索引器T
或T
不是override
声明且可在当前上下文中访问 (成员访问) 的基类型。The set consists of all indexers declared inT
or a base type ofT
that are notoverride
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 indexerS.I
in the set, whereS
is the type in which the indexerI
is declared:- 如果不适
I
用于A
(适用的函数成员) ,则I
将从集合中删除。IfI
is not applicable with respect toA
(Applicable function member), thenI
is removed from the set. - 如果
I
适用于A
(适用的函数成员) ,则将从集合中删除在的基类型中声明的所有索引器S
。IfI
is applicable with respect toA
(Applicable function member), then all indexers declared in a base type ofS
are removed from the set. - 如果
I
适用于A
(适用的函数成员) 并且是以外S
的类类型,则将object
从集合中删除在接口中声明的所有索引器。IfI
is applicable with respect toA
(Applicable function member) andS
is a class type other thanobject
, 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 ofP
and an associated argument list ofA
.
索引器访问会导致索引器的 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_access 。A this_access is permitted only in the block of an instance constructor, an instance method, or an instance accessor. 它具有下列含义之一:It has one of the following meanings:
- 当
this
用于类的实例构造函数中的 primary_expression 时,它会归类为值。Whenthis
is used in a primary_expression within an instance constructor of a class, it is classified as a value. 值的类型是类 的实例类型) ( 实例类型,在此类中发生了使用,值是对要构造的对象的引用。The type of the value is the instance type (The instance type) of the class within which the usage occurs, and the value is a reference to the object being constructed. - 当
this
用于类的实例方法或实例访问器中的 primary_expression 时,它将被分类为值。Whenthis
is used in a primary_expression within an instance method or instance accessor of a class, it is classified as a value. 值的类型为实例类型 (实例类型) 在其中发生使用的类的 实例 类型,值是对为其调用方法或访问器的对象的引用。The type of the value is the instance type (The instance type) of the class within which the usage occurs, and the value is a reference to the object for which the method or accessor was invoked. - 当
this
用于结构的实例构造函数中的 primary_expression 时,它会归类为变量。Whenthis
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
具体而言,这意味着必须在实例构造函数的每个执行路径中明确赋值变量。Thethis
variable of an instance constructor of a struct behaves exactly the same as anout
parameter of the struct type—in particular, this means that the variable must be definitely assigned in every execution path of the instance constructor. - 当
this
用于结构的实例方法或实例访问器中的 primary_expression 时,它会归类为变量。Whenthis
is used in a primary_expression within an instance method or instance accessor of a struct, it is classified as a variable. 变量的类型是实例类型 (在其中发生使用的结构的 实例类型) 。The type of the variable is the instance type (The instance type) of the struct within which the usage occurs.- 如果方法或访问器不是迭代 器) (迭代器 ,则该
this
变量表示调用了方法或访问器的结构,其行为与ref
结构类型的参数完全相同。If the method or accessor is not an iterator (Iterators), thethis
variable represents the struct for which the method or accessor was invoked, and behaves exactly the same as aref
parameter of the struct type. - 如果方法或访问器是迭代器,则该
this
变量表示为其调用了方法或访问器的结构的副本,其行为与结构类型的值参数完全相同。If the method or accessor is an iterator, thethis
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.
- 如果方法或访问器不是迭代 器) (迭代器 ,则该
如果 this
在上面列出的上下文之外的 primary_expression 中使用了,则会发生编译时错误。Use of this
in a primary_expression in a context other than the ones listed above is a compile-time error. 特别是,不能在 this
静态方法、静态属性访问器或字段声明的 variable_initializer 中引用。In particular, it is not possible to refer to this
in a static method, a static property accessor, or in a variable_initializer of a field declaration.
基本访问权限Base access
Base_access 包含保留字 base
,后面跟有 " .
" 标记、标识符或用方括号括起来的 argument_list :A 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_access 。A base_access is permitted only in the block of an instance constructor, an instance method, or an instance accessor. base.I
在类或结构中发生时, I
必须表示该类或结构的基类的成员。When base.I
occurs in a class or struct, I
must denote a member of the base class of that class or struct. 同样, base[E]
在类中发生时,基类中必须存在适用的索引器。Likewise, when base[E]
occurs in a class, an applicable indexer must exist in the base class.
在绑定时,该窗体的 base_access 表达式 base.I
的 base[E]
计算方式与写入它们的方式完全 ((B)this).I
相同 ((B)this)[E]
,其中 B
是发生构造的类或结构的基类。At binding-time, base_access expressions of the form base.I
and base[E]
are evaluated exactly as if they were written ((B)this).I
and ((B)this)[E]
, where B
is the base class of the class or struct in which the construct occurs. 因此, base.I
和 base[E]
对应于 this.I
和 this[E]
,只不过 this
将视为基类的一个实例。Thus, base.I
and base[E]
correspond to this.I
and this[E]
, except this
is viewed as an instance of the base class.
如果 base_access (方法、属性或索引器) 引用虚拟函数成员,则在运行时确定要调用哪个函数成员 (对 动态重载决策) 的编译时检查 会发生更改。When a base_access references a virtual function member (a method, property, or indexer), the determination of which function member to invoke at run-time (Compile-time checking of dynamic overload resolution) is changed. 所调用的函数成员是通过 查找) 函数 (成员的派生程度最高的 (实现来确定的 B
,而不是与运行时类型相关,而不是 this
像在非基本访问) 中那样常见。The function member that is invoked is determined by finding the most derived implementation (Virtual methods) of the function member with respect to B
(instead of with respect to the run-time type of this
, as would be usual in a non-base access). 因此,在 override
virtual
函数成员的中,可以使用 base_access 来调用函数成员的继承实现。Thus, within an override
of a virtual
function member, a base_access can be used to invoke the inherited implementation of the function member. 如果 base_access 引用的函数成员是抽象的,则会发生绑定时错误。If the function member referenced by a base_access is abstract, a binding-time error occurs.
后缀增量和减量运算符Postfix increment and decrement operators
post_increment_expression
: primary_expression '++'
;
post_decrement_expression
: primary_expression '--'
;
后缀递增或递减运算的操作数必须是分类为变量、属性访问或索引器访问的表达式。The operand of a postfix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. 操作的结果是与操作数类型相同的值。The result of the operation is a value of the same type as the operand.
如果 primary_expression 具有编译时类型 dynamic
,则运算符会动态绑定 (动态绑定) , post_increment_expression 或 post_decrement_expression 具有编译时类型, dynamic
并且以下规则在运行时使用 primary_expression 的运行时类型应用。If the primary_expression has the compile-time type dynamic
then the operator is dynamically bound (Dynamic binding), the post_increment_expression or post_decrement_expression has the compile-time type dynamic
and the following rules are applied at run-time using the run-time type of the primary_expression.
如果后缀递增或递减运算的操作数是属性或索引器访问,则属性或索引器必须同时具有 get
和 set
访问器。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. 预定义 ++
和 --
运算符存在以下类型:、、、、、、、、、、、 sbyte
byte
short
ushort
int
uint
long
ulong
char
float
double
decimal
和枚举类型。Predefined ++
and --
operators exist for the following types: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
, and any enum type. 预定义 ++
运算符返回通过向操作数添加1而生成的值,预定义 --
运算符返回通过从操作数中减去1而生成的值。The predefined ++
operators return the value produced by adding 1 to the operand, and the predefined --
operators return the value produced by subtracting 1 from the operand. 在 checked
上下文中,如果此加法或减法的结果超出了结果类型的范围,且结果类型为整型类型或枚举类型,则 System.OverflowException
会引发。In a checked
context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a System.OverflowException
is thrown.
此形式的后缀递增或递减运算的运行时处理, x++
或 x--
由以下步骤组成:The run-time processing of a postfix increment or decrement operation of the form x++
or x--
consists of the following steps:
- 如果
x
归类为变量:Ifx
is classified as a variable:x
计算以生成变量。x
is evaluated to produce the variable.- 的值已
x
保存。The value ofx
is saved. - 调用所选运算符时,将的值
x
作为其参数。The selected operator is invoked with the saved value ofx
as its argument. - 运算符返回的值存储在的计算给定的位置
x
。The value returned by the operator is stored in the location given by the evaluation ofx
. - 保存的值
x
将成为操作的结果。The saved value ofx
becomes the result of the operation.
- 如果
x
归类为属性或索引器访问:Ifx
is classified as a property or indexer access:- 如果) 为,则实例表达式 (如果是,则为
x
static
x
; 如果是与关联的索引器访问) ,则 (参数列表x
,并且在后续get
set
调用和访问器调用中使用结果。The instance expression (ifx
is notstatic
) and the argument list (ifx
is an indexer access) associated withx
are evaluated, and the results are used in the subsequentget
andset
accessor invocations. get
调用的访问器x
,并保存返回值。Theget
accessor ofx
is invoked and the returned value is saved.- 调用所选运算符时,将的值
x
作为其参数。The selected operator is invoked with the saved value ofx
as its argument. set
调用的访问器时,将x
使用由运算符返回的值作为其value
参数。Theset
accessor ofx
is invoked with the value returned by the operator as itsvalue
argument.- 保存的值
x
将成为操作的结果。The saved value ofx
becomes the result of the operation.
- 如果) 为,则实例表达式 (如果是,则为
++
和 --
运算符还支持前缀符号 (前缀增量和减量运算符) 。The ++
and --
operators also support prefix notation (Prefix increment and decrement operators). 通常,或的结果 x++
x--
是操作之前的值 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.
operator ++
operator --
可以使用后缀或前缀表示法来调用或实现。An operator ++
or operator --
implementation can be invoked using either postfix or prefix notation. 对于这两个表示法,不能有单独的运算符实现。It is not possible to have separate operator implementations for the two notations.
调用 new 运算符The new operator
new
运算符用于创建类型的新实例。The new
operator is used to create new instances of types.
有三种形式的 new
表达式:There are three forms of new
expressions:
- 对象创建表达式用于创建类类型和值类型的新实例。Object creation expressions are used to create new instances of class types and value types.
- 数组创建表达式用于创建数组类型的新实例。Array creation expressions are used to create new instances of array types.
- 委托创建表达式用于创建委托类型的新实例。Delegate creation expressions are used to create new instances of delegate types.
new
运算符表示创建类型的实例,但不一定表示动态分配内存。The new
operator implies creation of an instance of a type, but does not necessarily imply dynamic allocation of memory. 具体而言,值类型的实例不需要超出它们所在的变量的额外内存,并且当 new
用于创建值类型的实例时,不会发生动态分配。In particular, instances of value types require no additional memory beyond the variables in which they reside, and no dynamic allocations occur when new
is used to create instances of value types.
对象创建表达式Object creation expressions
Object_creation_expression 用于创建 class_type 或 value_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_type、 value_type 或 type_parameter。The type of an object_creation_expression must be a class_type, a value_type or a type_parameter. 类型 不能是 abstract
class_type。The type cannot be an abstract
class_type.
仅当 类型 为 class_type 或 struct_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 的这些参数的运行时类型在运行时应用以下规则 dynamic
。If 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_type 或 value_type ,并且 A
是可选的 argument_list,包括以下步骤:The binding-time processing of an object_creation_expression of the form new T(A)
, where T
is a class_type or a value_type and A
is an optional argument_list, consists of the following steps:
- 如果
T
是 value_type 且A
不存在:IfT
is a value_type andA
is not present:- Object_creation_expression 是默认的构造函数调用。The object_creation_expression is a default constructor invocation. Object_creation_expression 的结果是类型为的值,它是
T
T
在 系统 ValueType 类型中定义的的默认值。The result of the object_creation_expression is a value of typeT
, namely the default value forT
as defined in The System.ValueType type.
- Object_creation_expression 是默认的构造函数调用。The object_creation_expression is a default constructor invocation. Object_creation_expression 的结果是类型为的值,它是
- 否则,如果
T
是 type_parameter 且A
不存在:Otherwise, ifT
is a type_parameter andA
is not present:- 如果没有为指定) 类型参数约束 (值类型约束或构造函数约束
T
,则会发生绑定时错误。If no value type constraint or constructor constraint (Type parameter constraints) has been specified forT
, 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.
- 如果没有为指定) 类型参数约束 (值类型约束或构造函数约束
- 否则,如果
T
是 class_type 或 struct_type:Otherwise, ifT
is a class_type or a struct_type:- 如果
T
是abstract
class_type,则会发生编译时错误。IfT
is anabstract
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 inT
which are applicable with respect toA
(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 typeT
, 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 是动态绑定的,编译时类型仍为 T
。Even if the object_creation_expression is dynamically bound, the compile-time type is still T
.
对 object_creation_expression 窗体的运行时处理 new T(A)
,其中 T
是 class_type 或 struct_type ,并且 A
是可选的 argument_list,包括以下步骤:The run-time processing of an object_creation_expression of the form new T(A)
, where T
is class_type or a struct_type and A
is an optional argument_list, consists of the following steps:
- 如果
T
是 class_type:IfT
is a class_type:- 分配类的新实例
T
。A new instance of classT
is allocated. 如果没有足够的内存可用于分配新的实例,System.OutOfMemoryException
则会引发,而不执行进一步的步骤。If there is not enough memory available to allocate the new instance, aSystem.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). 对新分配的实例的引用会自动传递到实例构造函数,并且可以从该构造函数中将实例作为进行访问
this
。A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor asthis
.
- 分配类的新实例
- 如果
T
是 struct_type:IfT
is a struct_type:T
通过分配临时本地变量来创建类型的实例。An instance of typeT
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). 对新分配的实例的引用会自动传递到实例构造函数,并且可以从该构造函数中将实例作为进行访问
this
。A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor asthis
.
对象初始值设定项Object initializers
对象初始值设定项 为零个或多个字段、属性或对象的索引元素指定值。An object initializer specifies values for zero or more fields, properties or indexed elements of an object.
object_initializer
: '{' member_initializer_list? '}'
| '{' member_initializer_list ',' '}'
;
member_initializer_list
: member_initializer (',' member_initializer)*
;
member_initializer
: initializer_target '=' initializer_value
;
initializer_target
: identifier
| '[' argument_list ']'
;
initializer_value
: expression
| object_or_collection_initializer
;
对象初始值设定项由成员初始值设定项的序列组成,括在和中,并用 {
}
逗号分隔。An object initializer consists of a sequence of member initializers, enclosed by {
and }
tokens and separated by commas. 每个 member_initializer 为初始化指定一个目标。Each member_initializer designates a target for the initialization. 标识符 必须命名要初始化的对象的可访问字段或属性,而用方括号括起来的 argument_list 必须在要初始化的对象上指定可访问索引器的参数。An identifier must name an accessible field or property of the object being initialized, whereas an argument_list enclosed in square brackets must specify arguments for an accessible indexer on the object being initialized. 对象初始值设定项为同一个字段或属性包含多个成员初始值设定项是错误的。It is an error for an object initializer to include more than one member initializer for the same field or property.
每个 initializer_target 后跟一个等号和一个表达式、一个对象初始值设定项或集合初始值设定项。Each initializer_target is followed by an equals sign and either an expression, an object initializer or a collection initializer. 对象初始值设定项中的表达式不能引用它要初始化的新创建的对象。It is not possible for expressions within the object initializer to refer to the newly created object it is initializing.
在处理等号后指定表达式的成员初始值设定项的处理方式与对目标 (简单的 赋值) 相同。A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (Simple assignment) to the target.
在等号后指定对象初始值设定项的成员初始值设定项是 嵌套的对象初始值设定 项,即嵌入对象的初始化。A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. 嵌套的对象初始值设定项中的赋值被视为对该字段或属性的成员的赋值,而不是为字段或属性赋值。Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. 嵌套的对象初始值设定项不能应用于值类型的属性,也不能应用于具有值类型的只读字段。Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
在等号后指定集合初始值设定项的成员初始值设定项是嵌入集合的初始化。A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. 将初始值设定项中给定的元素添加到目标所引用的集合,而不是将新集合分配给目标字段、属性或索引器。Instead of assigning a new collection to the target field, property or indexer, the elements given in the initializer are added to the collection referenced by the target. 目标必须为满足 集合初始值设定项中指定的要求的集合类型。The target must be of a collection type that satisfies the requirements specified in Collection initializers.
索引初始值设定项的参数将始终只计算一次。The arguments to an index initializer will always be evaluated exactly once. 因此,即使参数最终不会使用 (例如,因为) 的嵌套初始值设定项为空,它们会对其副作用进行评估。Thus, even if the arguments end up never getting used (e.g. because of an empty nested initializer), they will be evaluated for their side effects.
下面的类表示一个具有两个坐标的点:The following class represents a point with two coordinates:
public class Point
{
int x, y;
public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
}
Point
可按如下所示创建和初始化实例:An instance of Point
can be created and initialized as follows:
Point a = new Point { X = 0, Y = 1 };
这与which has the same effect as
Point __a = new Point();
__a.X = 0;
__a.Y = 1;
Point a = __a;
其中 __a
,是其他不可见且无法访问的临时变量。where __a
is an otherwise invisible and inaccessible temporary variable. 下面的类表示从两个点创建的矩形:The following class represents a rectangle created from two points:
public class Rectangle
{
Point p1, p2;
public Point P1 { get { return p1; } set { p1 = value; } }
public Point P2 { get { return p2; } set { p2 = value; } }
}
Rectangle
可按如下所示创建和初始化实例:An instance of Rectangle
can be created and initialized as follows:
Rectangle r = new Rectangle {
P1 = new Point { X = 0, Y = 1 },
P2 = new Point { X = 2, Y = 3 }
};
这与which has the same effect as
Rectangle __r = new Rectangle();
Point __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
__r.P1 = __p1;
Point __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
__r.P2 = __p2;
Rectangle r = __r;
其中 __r
, __p1
和 __p2
是在其他情况下不可见且不可访问的临时变量。where __r
, __p1
and __p2
are temporary variables that are otherwise invisible and inaccessible.
If Rectangle
的构造函数分配两个嵌入的 Point
实例If Rectangle
's constructor allocates the two embedded Point
instances
public class Rectangle
{
Point p1 = new Point();
Point p2 = new Point();
public Point P1 { get { return p1; } }
public Point P2 { get { return p2; } }
}
以下构造可用于初始化嵌入 Point
实例,而不是分配新实例:the following construct can be used to initialize the embedded Point
instances instead of assigning new instances:
Rectangle r = new Rectangle {
P1 = { X = 0, Y = 1 },
P2 = { X = 2, Y = 3 }
};
这与which has the same effect as
Rectangle __r = new Rectangle();
__r.P1.X = 0;
__r.P1.Y = 1;
__r.P2.X = 2;
__r.P2.Y = 3;
Rectangle r = __r;
给定 C 的适当定义,以下示例:Given an appropriate definition of C, the following example:
var c = new C {
x = true,
y = { a = "Hello" },
z = { 1, 2, 3 },
["x"] = 5,
[0,0] = { "a", "b" },
[1,2] = {}
};
等效于这一系列的赋值:is equivalent to this series of assignments:
C __c = new C();
__c.x = true;
__c.y.a = "Hello";
__c.z.Add(1);
__c.z.Add(2);
__c.z.Add(3);
string __i1 = "x";
__c[__i1] = 5;
int __i2 = 0, __i3 = 0;
__c[__i2,__i3].Add("a");
__c[__i2,__i3].Add("b");
int __i4 = 1, __i5 = 2;
var c = __c;
where __c
,等等,生成的变量不可见且无法访问源代码。where __c
, etc., are generated variables that are invisible and inaccessible to the source code. 请注意,的参数 [0,0]
只计算一次,并且的参数只 [1,2]
计算一次,即使从未使用过它们也是如此。Note that the arguments for [0,0]
are evaluated only once, and the arguments for [1,2]
are evaluated once even though they are never used.
集合初始值设定项Collection initializers
集合初始值设定项指定集合的元素。A collection initializer specifies the elements of a collection.
collection_initializer
: '{' element_initializer_list '}'
| '{' element_initializer_list ',' '}'
;
element_initializer_list
: element_initializer (',' element_initializer)*
;
element_initializer
: non_assignment_expression
| '{' expression_list '}'
;
expression_list
: expression (',' expression)*
;
集合初始值设定项由元素初始值设定项序列组成,括在和中,并用 {
}
逗号分隔。A collection initializer consists of a sequence of element initializers, enclosed by {
and }
tokens and separated by commas. 每个元素初始值设定项指定一个元素,该元素将被添加到要初始化的集合对象中,并由括在 {
和 }
标记中并以逗号分隔的表达式列表组成。Each element initializer specifies an element to be added to the collection object being initialized, and consists of a list of expressions enclosed by {
and }
tokens and separated by commas. 单表达式元素初始值设定项可以不带大括号编写,但是不能是赋值表达式,以避免成员初始值设定项出现歧义。A single-expression element initializer can be written without braces, but cannot then be an assignment expression, to avoid ambiguity with member initializers. Non_assignment_expression 生产是在 expression中定义的。The non_assignment_expression production is defined in Expression.
下面是包含集合初始值设定项的对象创建表达式的示例:The following is an example of an object creation expression that includes a collection initializer:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
集合初始值设定项应用于的集合对象必须是实现 System.Collections.IEnumerable
或发生编译时错误的类型。The collection object to which a collection initializer is applied must be of a type that implements System.Collections.IEnumerable
or a compile-time error occurs. 对于按顺序排列的每个指定元素,集合初始值设定项 Add
使用元素初始值设定项的表达式列表作为参数列表调用目标对象上的方法,并对每个调用应用常规成员查找和重载解析。For each specified element in order, the collection initializer invokes an Add
method on the target object with the expression list of the element initializer as argument list, applying normal member lookup and overload resolution for each invocation. 因此,集合对象必须具有适用于 Add
每个元素初始值设定项的名称的适用实例或扩展方法。Thus, the collection object must have an applicable instance or extension method with the name Add
for each element initializer.
下面的类表示一个联系人,其中包含名称和电话号码的列表:The following class represents a contact with a name and a list of phone numbers:
public class Contact
{
string name;
List<string> phoneNumbers = new List<string>();
public string Name { get { return name; } set { name = value; } }
public List<string> PhoneNumbers { get { return phoneNumbers; } }
}
List<Contact>
可以按如下所示创建和初始化:A List<Contact>
can be created and initialized as follows:
var contacts = new List<Contact> {
new Contact {
Name = "Chris Smith",
PhoneNumbers = { "206-555-0101", "425-882-8080" }
},
new Contact {
Name = "Bob Harris",
PhoneNumbers = { "650-555-0199" }
}
};
这与which has the same effect as
var __clist = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
__clist.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
__clist.Add(__c2);
var contacts = __clist;
其中 __clist
, __c1
和 __c2
是在其他情况下不可见且不可访问的临时变量。where __clist
, __c1
and __c2
are temporary variables that are otherwise invisible and inaccessible.
数组创建表达式Array creation expressions
Array_creation_expression 用于创建 array_type 的新实例。An array_creation_expression is used to create a new instance of an array_type.
array_creation_expression
: 'new' non_array_type '[' expression_list ']' rank_specifier* array_initializer?
| 'new' array_type array_initializer
| 'new' rank_specifier array_initializer
;
第一种形式的数组创建表达式将分配从表达式列表中删除每个表达式所得到的类型的数组实例。An array creation expression of the first form allocates an array instance of the type that results from deleting each of the individual expressions from the expression list. 例如,数组创建表达式 new int[10,20]
生成类型为的数组实例 int[,]
,数组创建表达式将 new int[10][,]
生成类型为的数组 int[][,]
。For example, the array creation expression new int[10,20]
produces an array instance of type int[,]
, and the array creation expression new int[10][,]
produces an array of type int[][,]
. 表达式列表中的每个表达式的类型必须为 int
、 uint
、 long
或 ulong
,或者可隐式转换为这些类型中的一个或多个。Each expression in the expression list must be of type int
, uint
, long
, or ulong
, or implicitly convertible to one or more of these types. 每个表达式的值决定了新分配的数组实例中相应维度的长度。The value of each expression determines the length of the corresponding dimension in the newly allocated array instance. 由于数组维度的长度必须为非负,因此在表达式列表中有一个具有负值的 constant_expression 编译时错误。Since the length of an array dimension must be nonnegative, it is a compile-time error to have a constant_expression with a negative value in the expression list.
除非在不安全的上下文中 (不安全 上下文) ,否则不指定数组的布局。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 expression s found in nested array_initializer s.
数组 初始值设定项中进一步介绍了数组初始值设定项。Array initializers are described further in Array initializers.
计算数组创建表达式的结果是一个值,即对新分配的数组实例的引用。The result of evaluating an array creation expression is classified as a value, namely a reference to the newly allocated array instance. 数组创建表达式的运行时处理包括以下步骤:The run-time processing of an array creation expression consists of the following steps:
- 将按从左到右的顺序计算 expression_list 的维度长度表达式。The dimension length expressions of the expression_list are evaluated in order, from left to right. 对每个表达式进行以下计算后,将执行隐式转换 (将 隐式 转换) 为以下类型之一:
int
、uint
、long
和ulong
。Following 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, aSystem.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, aSystem.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[]
. 每个元素的初始值均为 null
。The initial value of each element is null
. 同一个数组创建表达式不可能同时实例化子数组和语句It is not possible for the same array creation expression to also instantiate the sub-arrays, and the statement
int[][] a = new int[100][5]; // Error
导致编译时错误。results in a compile-time error. 子数组的实例化必须改为手动执行,如下所示Instantiation of the sub-arrays must instead be performed manually, as in
int[][] a = new int[100][];
for (int i = 0; i < 100; i++) a[i] = new int[5];
当数组的数组具有 "矩形" 形状时,即当子数组的长度完全相同时,使用多维数组更为有效。When an array of arrays has a "rectangular" shape, that is when the sub-arrays are all of the same length, it is more efficient to use a multi-dimensional array. 在上面的示例中,数组数组的实例化将创建101对象,即一个外部数组和100子数组。In the example above, instantiation of the array of arrays creates 101 objects—one outer array and 100 sub-arrays. 相反,In contrast,
int[,] = new int[100, 5];
仅创建单个对象和二维数组,并在单个语句中完成分配。creates only a single object, a two-dimensional array, and accomplishes the allocation in a single statement.
下面是隐式类型的数组创建表达式的示例:The following are examples of implicitly typed array creation expressions:
var a = new[] { 1, 10, 100, 1000 }; // int[]
var b = new[] { 1, 1.5, 2, 2.5 }; // double[]
var c = new[,] { { "hello", null }, { "world", "!" } }; // string[,]
var d = new[] { 1, "one", 2, "two" }; // Error
最后一个表达式会导致编译时错误,因为和都不能 int
string
隐式转换为另一个表达式,因此没有最常见的类型。The last expression causes a compile-time error because neither int
nor string
is implicitly convertible to the other, and so there is no best common type. 在这种情况下,必须使用显式类型化的数组创建表达式,例如,将类型指定为 object[]
。An explicitly typed array creation expression must be used in this case, for example specifying the type to be object[]
. 或者,可以将其中一个元素强制转换为公共基类型,后者随后将成为推断元素类型。Alternatively, one of the elements can be cast to a common base type, which would then become the inferred element type.
隐式类型的数组创建表达式可与匿名对象初始值设定项组合 (匿名对象创建表达式) 创建匿名类型的数据结构。Implicitly typed array creation expressions can be combined with anonymous object initializers (Anonymous object creation expressions) to create anonymously typed data structures. 例如:For example:
var contacts = new[] {
new {
Name = "Chris Smith",
PhoneNumbers = new[] { "206-555-0101", "425-882-8080" }
},
new {
Name = "Bob Harris",
PhoneNumbers = new[] { "650-555-0199" }
}
};
委托创建表达式Delegate creation expressions
Delegate_creation_expression 用于创建 delegate_type 的新实例。A delegate_creation_expression is used to create a new instance of a delegate_type.
delegate_creation_expression
: 'new' delegate_type '(' expression ')'
;
委托创建表达式的参数必须是方法组、匿名函数或编译时类型 dynamic
或 delegate_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_type 并且 E
是一个 表达式,它包含以下步骤:The binding-time processing of a delegate_creation_expression of the form new D(E)
, where D
is a delegate_type and E
is an expression, consists of the following steps:
- 如果
E
是方法组,则以与方法组转换 (方法 组转换相同的方式处理委托创建表达式,) 从转换E
为D
。IfE
is a method group, the delegate creation expression is processed in the same way as a method group conversion (Method group conversions) fromE
toD
. - 如果
E
是匿名函数,则用与匿名函数转换相同的方式处理委托创建表达式,) 从到 (匿名函数 转换E
D
。IfE
is an anonymous function, the delegate creation expression is processed in the same way as an anonymous function conversion (Anonymous function conversions) fromE
toD
. - 如果
E
是值,则E
必须与) (委托声明 兼容D
,并且结果是对类型为的新创建的委托的引用,该委托D
引用与相同的调用列表E
。IfE
is a value,E
must be compatible (Delegate declarations) withD
, and the result is a reference to a newly created delegate of typeD
that refers to the same invocation list asE
. 如果与E
不兼容D
,则会发生编译时错误。IfE
is not compatible withD
, a compile-time error occurs.
窗体的 delegate_creation_expression 的运行时处理 new D(E)
,其中 D
是一个 delegate_type 并且 E
是一个 表达式,它包含以下步骤:The run-time processing of a delegate_creation_expression of the form new D(E)
, where D
is a delegate_type and E
is an expression, consists of the following steps:
- 如果
E
是方法组,则将委托创建表达式作为方法组转换进行计算, (方法组 将) 从转换E
为D
。IfE
is a method group, the delegate creation expression is evaluated as a method group conversion (Method group conversions) fromE
toD
. - 如果
E
是匿名函数,则会将委托创建作为从E
到D
(匿名函数 转换的匿名函数转换进行计算) 。IfE
is an anonymous function, the delegate creation is evaluated as an anonymous function conversion fromE
toD
(Anonymous function conversions). - 如果
E
是 delegate_type 的值:IfE
is a value of a delegate_type:E
进行计算。E
is evaluated. 如果此计算导致异常,则不执行其他步骤。If this evaluation causes an exception, no further steps are executed.- 如果的值
E
为null
,则System.NullReferenceException
会引发,而不执行进一步的步骤。If the value ofE
isnull
, aSystem.NullReferenceException
is thrown and no further steps are executed. - 分配委托类型的新实例
D
。A new instance of the delegate typeD
is allocated. 如果没有足够的内存可用于分配新的实例,System.OutOfMemoryException
则会引发,而不执行进一步的步骤。If there is not enough memory available to allocate the new instance, aSystem.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 byE
.
委托的调用列表是在对委托进行实例化时确定的,然后在该委托的整个生存期内保持不变。The invocation list of a delegate is determined when the delegate is instantiated and then remains constant for the entire lifetime of the delegate. 换句话说,创建委托后,不能更改其目标可调用的实体。In other words, it is not possible to change the target callable entities of a delegate once it has been created. 合并两个委托或从另一个 (委托) 声明 中移除一个委托时,将返回一个新的委托结果;现有委托的内容未发生更改。When two delegates are combined or one is removed from another (Delegate declarations), a new delegate results; no existing delegate has its contents changed.
不能创建引用属性、索引器、用户定义的运算符、实例构造函数、析构函数或静态构造函数的委托。It is not possible to create a delegate that refers to a property, indexer, user-defined operator, instance constructor, destructor, or static constructor.
如上所述,从方法组创建委托时,委托的形参列表和返回类型确定要选择的重载方法。As described above, when a delegate is created from a method group, the formal parameter list and return type of the delegate determine which of the overloaded methods to select. 示例中In the example
delegate double DoubleFunc(double x);
class A
{
DoubleFunc f = new DoubleFunc(Square);
static float Square(float x) {
return x * x;
}
static double Square(double x) {
return x * x;
}
}
A.f
使用引用第二种方法的委托来初始化字段 Square
,因为该方法与的形参列表和返回类型完全匹配 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. 匿名类型是直接从继承的无类类类型 object
。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() { ... }
}
其中每个 Tx
是对应的表达式的类型 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;
允许在最后一行上进行赋值,因为 p1
和 p2
具有相同的匿名类型。the assignment on the last line is permitted because p1
and p2
are of the same anonymous type.
Equals
GetHashcode
匿名类型上的和方法会重写从继承的方法 object
,并根据属性的和进行定义 Equals
GetHashcode
,因此,当且仅当所有属性都相等时,相同匿名类型的两个实例才相等。The Equals
and GetHashcode
methods on anonymous types override the methods inherited from object
, and are defined in terms of the Equals
and GetHashcode
of the properties, so that two instances of the same anonymous type are equal if and only if all their properties are equal.
成员声明符可以缩写为简单名称 (类型推理) ,成员访问 (动态重载决策的编译时检查) ,基本访问 (基本 访问) 或 null 条件成员访问 (为) 的 投影初始值设定项 。A member declarator can be abbreviated to a simple name (Type inference), a member access (Compile-time checking of dynamic overload resolution), a base access (Base access) or a null-conditional member access (Null-conditional expressions as projection initializers). 这称为 投影初始值设定项 ,是的声明和对具有相同名称的属性赋值的简写形式。This is called a projection initializer and is shorthand for a declaration of and assignment to a property with the same name. 具体而言,是窗体的成员声明符Specifically, member declarators of the forms
identifier
expr.identifier
与以下各项完全等效:are precisely equivalent to the following, respectively:
identifier = identifier
identifier = expr.identifier
因此,在投影初始值设定项中, 标识符 既选择值,又选择要向其分配值的字段或属性。Thus, in a projection initializer the identifier selects both the value and the field or property to which the value is assigned. 直观而言,投影初始值设定项不只是一个值,而是值的名称。Intuitively, a projection initializer projects not just a value, but also the name of the value.
Typeof 运算符The typeof operator
typeof
运算符用于获取 System.Type
类型的对象。The typeof
operator is used to obtain the System.Type
object for a type.
typeof_expression
: 'typeof' '(' type ')'
| 'typeof' '(' unbound_type_name ')'
| 'typeof' '(' 'void' ')'
;
unbound_type_name
: identifier generic_dimension_specifier?
| identifier '::' identifier generic_dimension_specifier?
| unbound_type_name '.' identifier generic_dimension_specifier?
;
generic_dimension_specifier
: '<' comma* '>'
;
comma
: ','
;
第一种形式的 typeof_expression 由 typeof
关键字后跟带括号的 类型 组成。The first form of typeof_expression consists of a typeof
keyword followed by a parenthesized type. 此窗体的表达式的结果是所 System.Type
指示类型的对象。The result of an expression of this form is the System.Type
object for the indicated type. System.Type
任何给定类型都只有一个对象。There is only one System.Type
object for any given type. 这意味着对于类型 T
,始终为 typeof(T) == typeof(T)
true。This means that for a type T
, typeof(T) == typeof(T)
is always true. 类型 不能为 dynamic
。The type cannot be dynamic
.
第二种形式的 typeof_expression 由 typeof
关键字后跟带括号的 unbound_type_name 组成。The second form of typeof_expression consists of a typeof
keyword followed by a parenthesized unbound_type_name. Unbound_type_name 与 Type_name (命名空间和类型) 名称相同,只不过 unbound_type_name 包含 generic_dimension_specifier 包含 type_name 的 type_argument_list。 An unbound_type_name is very similar to a type_name (Namespace and type names) except that an unbound_type_name contains generic_dimension_specifier s where a type_name contains type_argument_list s. 当 typeof_expression 的操作数为满足 unbound_type_name 和 type_name 语法的标记序列时,即当它既不包含 generic_dimension_specifier 也不包含 type_argument_list 时,将标记序列视为 type_name。When the operand of a typeof_expression is a sequence of tokens that satisfies the grammars of both unbound_type_name and type_name, namely when it contains neither a generic_dimension_specifier nor a type_argument_list, the sequence of tokens is considered to be a type_name. 确定 unbound_type_name 的含义,如下所示:The meaning of an unbound_type_name is determined as follows:
- 通过将每个 generic_dimension_specifier 替换为与每个 type_argument 具有相同数量的逗号和关键字的 type_argument_list ,将标记序列转换为 type_name
object
。 Convert the sequence of tokens to a type_name by replacing each generic_dimension_specifier with a type_argument_list having the same number of commas and the keywordobject
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. 此形式的表达式的结果是 System.Type
表示缺少类型的对象。The result of an expression of this form is the System.Type
object that represents the absence of a type. 返回的类型对象 typeof(void)
不同于为任何类型返回的类型对象。The type object returned by typeof(void)
is distinct from the type object returned for any type. 此特殊类型对象在允许反射到语言中的方法的类库中很有用,在这种情况下,这些方法希望有一种方法来表示任何方法(包括 void 方法)的返回类型以及实例 System.Type
。This special type object is useful in class libraries that allow reflection onto methods in the language, where those methods wish to have a way to represent the return type of any method, including void methods, with an instance of System.Type
.
typeof
运算符可在类型参数上使用。The typeof
operator can be used on a type parameter. 结果是 System.Type
绑定到类型参数的运行时类型的对象。The result is the System.Type
object for the run-time type that was bound to the type parameter. typeof
运算符还可用于构造类型或未绑定的泛型类型 (绑定和未绑定的类型) 。The typeof
operator can also be used on a constructed type or an unbound generic type (Bound and unbound types). System.Type
未绑定的泛型类型的对象与实例类型的对象不同 System.Type
。The System.Type
object for an unbound generic type is not the same as the System.Type
object of the instance type. 实例类型在运行时始终是封闭式构造类型,因此它的 System.Type
对象依赖于正在使用中的运行时类型参数,而未绑定的泛型类型没有类型参数。The instance type is always a closed constructed type at run-time so its System.Type
object depends on the run-time type arguments in use, while the unbound generic type has no type arguments.
示例The example
using System;
class X<T>
{
public static void PrintTypes() {
Type[] t = {
typeof(int),
typeof(System.Int32),
typeof(string),
typeof(double[]),
typeof(void),
typeof(T),
typeof(X<T>),
typeof(X<X<T>>),
typeof(X<>)
};
for (int i = 0; i < t.Length; i++) {
Console.WriteLine(t[i]);
}
}
}
class Test
{
static void Main() {
X<int>.PrintTypes();
}
}
生成以下输出:produces the following output:
System.Int32
System.Int32
System.String
System.Double[]
System.Void
System.Int32
X`1[System.Int32]
X`1[X`1[System.Int32]]
X`1[T]
请注意, int
和 System.Int32
是相同的类型。Note that int
and System.Int32
are the same type.
另请注意,的结果 typeof(X<>)
不取决于类型参数,但的结果是 typeof(X<T>)
。Also note that the result of typeof(X<>)
does not depend on the type argument but the result of typeof(X<T>)
does.
checked 和 unchecked 运算符The checked and unchecked operators
checked
和 unchecked
运算符用于控制整型类型算术运算和转换的 溢出检查上下文。The checked
and unchecked
operators are used to control the overflow checking context for integral-type arithmetic operations and conversions.
checked_expression
: 'checked' '(' expression ')'
;
unchecked_expression
: 'unchecked' '(' expression ')'
;
checked
运算符在已检查的上下文中计算包含的表达式, unchecked
运算符在未检查的上下文中计算包含的表达式。The checked
operator evaluates the contained expression in a checked context, and the unchecked
operator evaluates the contained expression in an unchecked context. Checked_expression 或 unchecked_expression 完全对应于 parenthesized_expression (带圆括号的表达式) ,只是在给定溢出检查上下文中计算包含的表达式。A checked_expression or unchecked_expression corresponds exactly to a parenthesized_expression (Parenthesized expressions), except that the contained expression is evaluated in the given overflow checking context.
还可以通过 checked
unchecked
) 检查和未 检查的语句 (的和语句来控制溢出检查上下文。The overflow checking context can also be controlled through the checked
and unchecked
statements (The checked and unchecked statements).
checked
和运算符和语句所建立的溢出检查上下文会影响以下操作 unchecked
:The following operations are affected by the overflow checking context established by the checked
and unchecked
operators and statements:
++
--
当操作数为整数类型时,预定义的和一元运算符 (后缀递增和递减运算符以及前缀增量和减量运算符) 。The predefined++
and--
unary operators (Postfix increment and decrement operators and Prefix increment and decrement operators), when the operand is of an integral type.-
当操作数为整数类型时,预定义的一元运算符 (一元负运算符) 。The predefined-
unary operator (Unary minus operator), when the operand is of an integral type.+
-
*
当两个操作数均为整型时,预定义的、、和/
二元运算符) (算术运算符。The predefined+
,-
,*
, and/
binary operators (Arithmetic operators), when both operands are of integral types.- 显式数值转换 (从一个整型类型到另一个整型类型或从
float
或double
到整型的显式数字转换) 。Explicit numeric conversions (Explicit numeric conversions) from one integral type to another integral type, or fromfloat
ordouble
to an integral type.
当上述操作之一产生的结果太大而无法在目标类型中表示时,执行该操作的上下文控制产生的行为: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 achecked
context, if the operation is a constant expression (Constant expressions), a compile-time error occurs. 否则,在运行时执行操作时,System.OverflowException
将引发。Otherwise, when the operation is performed at run-time, aSystem.OverflowException
is thrown. - 在
unchecked
上下文中,将放弃不适合目标类型的任何高序位,从而截断结果。In anunchecked
context, the result is truncated by discarding any high-order bits that do not fit in the destination type.
对于非常量表达式 (在运行时计算的表达式(不是由任何 checked
或运算符或语句括起来的) unchecked
), unchecked
除非外部因素 (如编译器开关和执行环境配置) 调用来进行 checked
求值。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.
对于可以在编译时完全计算) (表达式的常量表达式,默认溢出检查上下文始终为 checked
。For constant expressions (expressions that can be fully evaluated at compile-time), the default overflow checking context is always checked
. 除非常量表达式显式放置在上下文中,否则在 unchecked
表达式的编译时计算过程中发生的溢出将始终导致编译时错误。Unless a constant expression is explicitly placed in an unchecked
context, overflows that occur during the compile-time evaluation of the expression always cause compile-time errors.
匿名函数的主体不受 checked
或在 unchecked
其中发生匿名函数的上下文影响。The body of an anonymous function is not affected by checked
or unchecked
contexts in which the anonymous function occurs.
示例中In the example
class Test
{
static readonly int x = 1000000;
static readonly int y = 1000000;
static int F() {
return checked(x * y); // Throws OverflowException
}
static int G() {
return unchecked(x * y); // Returns -727379968
}
static int H() {
return x * y; // Depends on default
}
}
不会报告编译时错误,因为在编译时无法计算两个表达式。no compile-time errors are reported since neither of the expressions can be evaluated at compile-time. 在运行时,方法会 F
引发 System.OverflowException
, G
方法返回-727379968 (超出范围的结果) 的低32位。At run-time, the F
method throws a System.OverflowException
, and the G
method returns -727379968 (the lower 32 bits of the out-of-range result). 方法的行为 H
取决于编译的默认溢出检查上下文,但与相同 F
或相同 G
。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
}
}
在中计算常数表达式时发生的溢出, F
H
导致报告编译时错误,因为在上下文中计算表达式 checked
。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
但由于计算发生在 unchecked
上下文中,因此不会报告溢出。An overflow also occurs when evaluating the constant expression in G
, but since the evaluation takes place in an unchecked
context, the overflow is not reported.
checked
和 unchecked
运算符仅对那些在 " (
" 和 "" 标记中包含的按 )
The checked
and unchecked
operators only affect the overflow checking context for those operations that are textually contained within the "(
" and ")
" tokens. 运算符对在计算包含的表达式时被调用的函数成员不起作用。The operators have no effect on function members that are invoked as a result of evaluating the contained expression. 示例中In the example
class Test
{
static int Multiply(int x, int y) {
return x * y;
}
static int F() {
return checked(Multiply(1000000, 1000000));
}
}
checked
在中使用不 F
会影响中的计算 x * y
Multiply
,因此 x * y
在默认溢出检查上下文中计算。the use of checked
in F
does not affect the evaluation of x * y
in Multiply
, so x * y
is evaluated in the default overflow checking context.
unchecked
用十六进制表示法编写带符号整数类型的常量时,运算符非常方便。The unchecked
operator is convenient when writing constants of the signed integral types in hexadecimal notation. 例如:For example:
class Test
{
public const int AllBits = unchecked((int)0xFFFFFFFF);
public const int HighBit = unchecked((int)0x80000000);
}
上述两个十六进制常量均为类型 uint
。Both of the hexadecimal constants above are of type uint
. 由于常量不在 int
此范围内,因此, unchecked
转换为 int
将产生编译时错误。Because the constants are outside the int
range, without the unchecked
operator, the casts to int
would produce compile-time errors.
checked
和 unchecked
运算符和语句使程序员能够控制某些数值计算的某些方面。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. 例如,两个小数相乘始终导致溢出异常,即使在显式构造中也是如此 unchecked
。For example, multiplying two decimals always results in an exception on overflow even within an explicitly unchecked
construct. 同样,将两个浮点数相乘会导致溢出异常,即使在显式构造中也是如此 checked
。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 是常量表达式:、、、、、、、、、、、、 sbyte
byte
short
ushort
int
uint
long
ulong
char
float
double
decimal
bool
或任何枚举类型。In addition, a default_value_expression is a constant expression if the type is one of the following value types: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
, bool
, or any enumeration type.
Nameof 表达式Nameof expressions
Nameof_expression 用于获取作为常量字符串的程序实体的名称。A nameof_expression is used to obtain the name of a program entity as a constant string.
nameof_expression
: 'nameof' '(' named_entity ')'
;
named_entity
: simple_name
| named_entity_target '.' identifier type_argument_list?
;
named_entity_target
: 'this'
| 'base'
| named_entity
| predefined_type
| qualified_alias_member
;
语法上说, named_entity 操作数始终是表达式。Grammatically speaking, the named_entity operand is always an expression. 由于不是 nameof
保留关键字,因此,nameof 表达式在语法上始终不明确,因为调用了简单名称 nameof
。Because 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_expression。Otherwise it is a nameof_expression.
Nameof_expression 的 named_entity 的含义是表达式的含义;即,作为 simple_name、 base_access 或 member_access。The 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 具有类型,则会发生编译时错误 dynamic
。It is a compile time error for a named_entity_target to have the type dynamic
.
Nameof_expression 是类型的常量表达式 string
,在运行时不起作用。A nameof_expression is a constant expression of type string
, and has no effect at runtime. 具体而言,它的 named_entity 不会进行评估,因此,在明确的赋值分析中将忽略它 () 简单表达式的常规规则 。Specifically, its named_entity is not evaluated, and is ignored for the purposes of definite assignment analysis (General rules for simple expressions). 它的值是可选的最终 type_argument_list 之前 named_entity 的最后一个标识符,转换方式如下:Its value is the last identifier of the named_entity before the optional final type_argument_list, transformed in the following way:
- 删除前缀 "
@
" (如果使用)。The prefix "@
", if used, is removed. - 每个 unicode_escape_sequence 都转换为其对应的 unicode 字符。Each unicode_escape_sequence is transformed into its corresponding Unicode character.
- 删除任何 formatting_characters 。Any 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
?
、、 +
-
、 !
、 ~
、 ++
、 --
、强制转换和 await
运算符称为一元运算符。The ?
, +
, -
, !
, ~
, ++
, --
, cast, and await
operators are called the unary operators.
unary_expression
: primary_expression
| null_conditional_expression
| '+' unary_expression
| '-' unary_expression
| '!' unary_expression
| '~' unary_expression
| pre_increment_expression
| pre_decrement_expression
| cast_expression
| await_expression
| unary_expression_unsafe
;
如果 unary_expression 的操作数具有编译时类型 dynamic
,则它 (动态 绑定) 动态绑定。If the operand of a unary_expression has the compile-time type dynamic
, it is dynamically bound (Dynamic binding). 在这种情况下, unary_expression 的编译时类型为 dynamic
,并将使用操作数的运行时类型在运行时进行以下描述的解决方法。In this case the compile-time type of the unary_expression is dynamic
, and the resolution described below will take place at run-time using the run-time type of the operand.
Null 条件运算符Null-conditional operator
仅当操作数为非 null 时,null 条件运算符才将操作列表应用于其操作数。The null-conditional operator applies a list of operations to its operand only if that operand is non-null. 否则,应用运算符的结果为 null
。Otherwise the result of applying the operator is null
.
null_conditional_expression
: primary_expression null_conditional_operations
;
null_conditional_operations
: null_conditional_operations? '?' '.' identifier type_argument_list?
| null_conditional_operations? '?' '[' argument_list ']'
| null_conditional_operations '.' identifier type_argument_list?
| null_conditional_operations '[' argument_list ']'
| null_conditional_operations '(' argument_list? ')'
;
操作列表可以包括成员访问和元素访问操作 (这些操作本身可能为 null 条件) ,以及调用。The list of operations can include member access and element access operations (which may themselves be null-conditional), as well as invocation.
例如,表达式 a.b?[0]?.c()
是具有 primary_expression 的 null_conditional_expression , a.b
null_conditional_operations ?[0]
(null 条件元素访问) , (?.c
null 条件成员访问) 和 ()
(调用) 。For example, the expression a.b?[0]?.c()
is a null_conditional_expression with a primary_expression a.b
and null_conditional_operations ?[0]
(null-conditional element access), ?.c
(null-conditional member access) and ()
(invocation).
对于 E
包含 primary_expression 的 null_conditional_expression P
,让我们以从上到的 E0
?
每个 null_conditional_operations 中删除前导的方式获取表达式 E
。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. 从概念上讲, E0
是指如果由 s 查找的 null 检查都不表示,将计算的表达式 ?
null
。Conceptually, E0
is the expression that will be evaluated if none of the null checks represented by the ?
s do find a null
.
同时,让我们 E1
通过 ?
以从上到 null_conditional_operations 的第一个中删除前导的方式来获取表达式 E
。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_expression,则这可能会导致主表达式 (。This may lead to a primary-expression (if there was just one ?
) or to another null_conditional_expression.
例如,如果 E
是表达式,则 a.b?[0]?.c()
为表达式, E0
a.b[0].c()
E1
是表达式 a.b[0]?.c()
。For example, if E
is the expression a.b?[0]?.c()
, then E0
is the expression a.b[0].c()
and E1
is the expression a.b[0]?.c()
.
如果 E0
归类为 nothing,则 E
分类为 nothing。If E0
is classified as nothing, then E
is classified as nothing. 否则,将 E 分类为值。Otherwise E is classified as a value.
E0
和 E1
用于确定的含义 E
:E0
and E1
are used to determine the meaning of E
:
如果
E
作为 statement_expression 出现,则的含义与E
语句相同IfE
occurs as a statement_expression the meaning ofE
is the same as the statementif ((object)P != null) E1;
但 P 只计算一次。except that P is evaluated only once.
否则,如果
E0
归类为 nothing,则会发生编译时错误。Otherwise, ifE0
is classified as nothing a compile-time error occurs.否则,请
T0
键入E0
。Otherwise, letT0
be the type ofE0
.如果
T0
是一个类型参数,该参数不知道是引用类型或不可为 null 的值类型,则会发生编译时错误。IfT0
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 的值类型,则的类型E
为T0?
,并且的含义与E
相同。IfT0
is a non-nullable value type, then the type ofE
isT0?
, and the meaning ofE
is the same as((object)P == null) ? (T0?)null : E1
除外,
P
只计算一次。except thatP
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 thatP
is evaluated only once.
如果 E1
本身是 null_conditional_expression,则将再次应用这些规则,将测试嵌套, null
直到没有更多 ?
的,并且表达式已缩小为主要表达式 E0
。If E1
is itself a null_conditional_expression, then these rules are applied again, nesting the tests for null
until there are no further ?
's, and the expression has been reduced all the way down to the primary-expression E0
.
例如,如果表达式 a.b?[0]?.c()
作为语句表达式出现,如语句中所示:For example, if the expression a.b?[0]?.c()
occurs as a statement-expression, as in the statement:
a.b?[0]?.c();
其含义等效于:its meaning is equivalent to:
if (a.b != null) a.b[0]?.c();
这同样等效于:which again is equivalent to:
if (a.b != null) if (a.b[0] != null) a.b[0].c();
除外, a.b
a.b[0]
只计算一次。Except that a.b
and a.b[0]
are evaluated only once.
如果它发生在使用其值的上下文中,如下所示:If it occurs in a context where its value is used, as in:
var x = a.b?[0]?.c();
并且假设最终调用的类型不是可以为 null 的值类型,其含义等效于:and assuming that the type of the final invocation is not a non-nullable value type, its meaning is equivalent to:
var x = (a.b == null) ? null : (a.b[0] == null) ? null : a.b[0].c();
除外, a.b
a.b[0]
只计算一次。except that a.b
and a.b[0]
are evaluated only once.
空条件表达式作为投影初始值设定项Null-conditional expressions as projection initializers
只允许将 null 条件表达式作为 anonymous_object_creation_expression (匿名对象创建表达式的 member_declarator ,) 如果它以 ((可为 null)条件) 成员访问结束。A null-conditional expression is only allowed as a member_declarator in an anonymous_object_creation_expression (Anonymous object creation expressions) if it ends with an (optionally null-conditional) member access. 语法上,此要求可表示为:Grammatically, this requirement can be expressed as:
null_conditional_member_access
: primary_expression null_conditional_operations? '?' '.' identifier type_argument_list?
| primary_expression null_conditional_operations '.' identifier type_argument_list?
;
这是上述 null_conditional_expression 语法的一种特殊情况。This is a special case of the grammar for null_conditional_expression above. 在 匿名对象创建表达式中 member_declarator 的生产仅包括 null_conditional_member_access。The production for member_declarator in Anonymous object creation expressions then includes only null_conditional_member_access.
空条件表达式作为语句表达式Null-conditional expressions as statement expressions
只允许将 null 条件表达式作为 statement_expression (expression 语句 ,) 如果它以调用结束。A null-conditional expression is only allowed as a statement_expression (Expression statements) if it ends with an invocation. 语法上,此要求可表示为:Grammatically, this requirement can be expressed as:
null_conditional_invocation_expression
: primary_expression null_conditional_operations '(' argument_list? ')'
;
这是上述 null_conditional_expression 语法的一种特殊情况。This is a special case of the grammar for null_conditional_expression above. 然后,表达式语句中 statement_expression 的生产仅包括 null_conditional_invocation_expression。The production for statement_expression in Expression statements then includes only null_conditional_invocation_expression.
一元加运算符Unary plus operator
对于该窗体的操作 +x
,一元运算符重载决策 (一元运算符重载决策 将应用于选择特定运算符实现) 。For an operation of the form +x
, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型是运算符的返回类型。The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. 预定义的一元加法运算符是:The predefined unary plus operators are:
int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);
对于每个运算符,结果只是操作数的值。For each of these operators, the result is simply the value of the operand.
一元减运算符Unary minus operator
对于该窗体的操作 -x
,一元运算符重载决策 (一元运算符重载决策 将应用于选择特定运算符实现) 。For an operation of the form -x
, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型是运算符的返回类型。The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. 预定义的求反运算符是:The predefined negation operators are:
整数取反:Integer negation:
int operator -(int x); long operator -(long x);
通过从零进行减计算来计算结果
x
。The result is computed by subtractingx
from zero. 如果的值x
为操作数类型的最小可表示值 (-2 ^ 31 用于int
或-2 ^ 63 (对于long
) ),则x
不能在操作数类型内表示。If the value ofx
is the smallest representable value of the operand type (-2^31 forint
or -2^63 forlong
), then the mathematical negation ofx
is not representable within the operand type. 如果在上下文中发生此情况checked
,则System.OverflowException
会引发; 如果发生在unchecked
上下文中,则结果为操作数的值并且不报告溢出。If this occurs within achecked
context, aSystem.OverflowException
is thrown; if it occurs within anunchecked
context, the result is the value of the operand and the overflow is not reported.如果求反运算符的操作数的类型为,则将
uint
其转换为类型long
,并且结果的类型为long
。If the operand of the negation operator is of typeuint
, it is converted to typelong
, and the type of the result islong
. 例外情况是,允许将int
2147483648 (-2 ^ 31) 值的规则作为十进制整数文本写入 (整数文本) 。An exception is the rule that permits theint
value -2147483648 (-2^31) to be written as a decimal integer literal (Integer literals).如果求反运算符的操作数为类型,则
ulong
会发生编译时错误。If the operand of the negation operator is of typeulong
, a compile-time error occurs. 例外情况是允许将long
值-9223372036854775808 (-2 ^ 63) 写入 (整数文本) 的十进制整数。An exception is the rule that permits thelong
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 ofx
with its sign inverted. 如果x
为 nan,则结果也为 nan。Ifx
is NaN, the result is also NaN.小数求反:Decimal negation:
decimal operator -(decimal x);
通过从零进行减计算来计算结果
x
。The result is computed by subtractingx
from zero. Decimal 求反等效于使用类型的一元减号运算符System.Decimal
。Decimal negation is equivalent to using the unary minus operator of typeSystem.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
,则结果为 false
。This operator computes the logical negation of the operand: If the operand is true
, the result is false
. 如果操作数为 false
,则结果为 true
。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);
对于每个运算符,操作的结果为的按位求补 x
。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)
不同之处在于,转换为时,将 E
始终像在 unchecked
上下文 (已检查和未检查的运算符) 一样执行。The result of evaluating ~x
, where x
is an expression of an enumeration type E
with an underlying type U
, is exactly the same as evaluating (E)(~(U)x)
, except that the conversion to E
is always performed as if in an unchecked
context (The checked and unchecked operators).
前缀增量和减量运算符Prefix increment and decrement operators
pre_increment_expression
: '++' unary_expression
;
pre_decrement_expression
: '--' unary_expression
;
前缀递增或递减运算的操作数必须是分类为变量、属性访问或索引器访问的表达式。The operand of a prefix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. 操作的结果是与操作数类型相同的值。The result of the operation is a value of the same type as the operand.
如果前缀递增或递减运算的操作数是属性或索引器访问,则属性或索引器必须同时具有 get
和 set
访问器。If the operand of a prefix increment or decrement operation is a property or indexer access, the property or indexer must have both a get
and a set
accessor. 如果不是这种情况,则会发生绑定时错误。If this is not the case, a binding-time error occurs.
一元运算符重载决策 (一元运算符重载决策 应用于选择特定运算符实现) 。Unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. 预定义 ++
和 --
运算符存在以下类型:、、、、、、、、、、、 sbyte
byte
short
ushort
int
uint
long
ulong
char
float
double
decimal
和枚举类型。Predefined ++
and --
operators exist for the following types: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
, and any enum type. 预定义 ++
运算符返回通过向操作数添加1而生成的值,预定义 --
运算符返回通过从操作数中减去1而生成的值。The predefined ++
operators return the value produced by adding 1 to the operand, and the predefined --
operators return the value produced by subtracting 1 from the operand. 在 checked
上下文中,如果此加法或减法的结果超出了结果类型的范围,且结果类型为整型类型或枚举类型,则 System.OverflowException
会引发。In a checked
context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a System.OverflowException
is thrown.
此形式的前缀递增或递减运算的运行时处理, ++x
或 --x
包含以下步骤:The run-time processing of a prefix increment or decrement operation of the form ++x
or --x
consists of the following steps:
- 如果
x
归类为变量:Ifx
is classified as a variable:x
计算以生成变量。x
is evaluated to produce the variable.- 将调用所选运算符,并将值
x
作为其参数。The selected operator is invoked with the value ofx
as its argument. - 运算符返回的值存储在的计算给定的位置
x
。The value returned by the operator is stored in the location given by the evaluation ofx
. - 运算符返回的值成为操作的结果。The value returned by the operator becomes the result of the operation.
- 如果
x
归类为属性或索引器访问:Ifx
is classified as a property or indexer access:- 如果) 为,则实例表达式 (如果是,则为
x
static
x
; 如果是与关联的索引器访问) ,则 (参数列表x
,并且在后续get
set
调用和访问器调用中使用结果。The instance expression (ifx
is notstatic
) and the argument list (ifx
is an indexer access) associated withx
are evaluated, and the results are used in the subsequentget
andset
accessor invocations. get
调用的访问器x
。Theget
accessor ofx
is invoked.- 使用访问器作为参数返回的值调用所选运算符
get
。The selected operator is invoked with the value returned by theget
accessor as its argument. set
调用的访问器时,将x
使用由运算符返回的值作为其value
参数。Theset
accessor ofx
is invoked with the value returned by the operator as itsvalue
argument.- 运算符返回的值成为操作的结果。The value returned by the operator becomes the result of the operation.
- 如果) 为,则实例表达式 (如果是,则为
++
和 --
运算符还支持后缀表示法, (后缀递增和递减运算符) 。The ++
and --
operators also support postfix notation (Postfix increment and decrement operators). 通常,或的结果 x++
x--
是操作之前的值 x
,而或的结果 ++x
--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.
operator++
operator--
可以使用后缀或前缀表示法来调用或实现。An operator++
or operator--
implementation can be invoked using either postfix or prefix notation. 对于这两个表示法,不能有单独的运算符实现。It is not possible to have separate operator implementations for the two notations.
强制转换表达式Cast expressions
Cast_expression 用于将表达式显式转换为给定类型。A cast_expression is used to explicitly convert an expression to a given type.
cast_expression
: '(' type ')' unary_expression
;
窗体的 cast_expression (T)E
,其中 T
是一个 类型 并且 E
是一个 unary_expression,它执行显式转换, (将的值) 到类型的 显式 转换 E
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
. 如果不存在从到的显式转换 E
T
,则发生绑定时错误。If no explicit conversion exists from E
to T
, a binding-time error occurs. 否则,结果为显式转换生成的值。Otherwise, the result is the value produced by the explicit conversion. 即使表示变量,结果也始终归类为值 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
additive_expression 或与计算值的 parenthesized_expression (组合 x - y)
在一起。For example, the expression (x)-y
could either be interpreted as a cast_expression (a cast of -y
to type x
) or as an additive_expression combined with a parenthesized_expression (which computes the value x - y)
.
若要解决 cast_expression 歧义,存在以下规则:只有在满足以下条件之一时,才会将包含在括号中的一个或多个 标记 () 空格 的序列视为 cast_expression 的开头:To resolve cast_expression ambiguities, the following rule exists: A sequence of one or more token s (White space) enclosed in parentheses is considered the start of a cast_expression only if at least one of the following are true:
- 标记序列对于 类型 是正确的语法,而不是 表达式 的语法。The sequence of tokens is correct grammar for a type, but not for an expression.
- 标记序列对于 类型 是正确的语法,紧跟在右括号后面的标记是标记 "
~
"、标记 ""、标记!
"(
"、 标识符 (Unicode 字符转义序列) Unicode 字符转义序列 、 文本 (文本) 或除和以外的任何 关键字 (关键字as
is
。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) exceptas
andis
.
上面的 "更正语法" 一词只是指标记序列必须符合特定的语法生产。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. 例如,如果 x
和 y
是标识符,则 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.
从消除歧义规则开始,如果 x
和是标识符、、cast_expression 和,则为,而 y
(x)y
(x)(y)
(x)(-y)
(x)-y
不是,即使标识一个类型,也是如此 x
。From the disambiguation rule it follows that, if x
and y
are identifiers, (x)y
, (x)(y)
, and (x)(-y)
are cast_expression s, but (x)-y
is not, even if x
identifies a type. 但是,如果 x
是标识预定义类型的关键字 (如 int
) ,则所有四个窗体 cast_expression s (,因为这种关键字) 本身不可能是表达式。However, if x
is a keyword that identifies a predefined type (such as int
), then all four forms are cast_expression s (because such a keyword could not possibly be an expression by itself).
Await 表达式Await expressions
Await 运算符用于暂停封闭异步函数的计算,直到操作数表示的异步操作完成。The await operator is used to suspend evaluation of the enclosing async function until the asynchronous operation represented by the operand has completed.
await_expression
: 'await' unary_expression
;
只允许在异步函数的主体中使用 await_expression () 异步函数 。An await_expression is only allowed in the body of an async function (Async functions). 在最近的封闭 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 内的大多数位置,因为这些是在语法上转换为使用非 async lambda 表达式。Note that an await_expression cannot occur in most places within a query_expression, because those are syntactically transformed to use non-async lambda expressions.
在异步函数内, await
不能用作标识符。Inside of an async function, await
cannot be used as an identifier. 因此,await 表达式和涉及标识符的各种表达式之间没有语法多义性。There is therefore no syntactic ambiguity between await-expressions and various expressions involving identifiers. 在异步函数的外部,作为 await
常规标识符。Outside of async functions, await
acts as a normal identifier.
Await_expression 的操作数称为 *task _。The operand of an await_expression is called the *task _. 它表示一个异步操作,该操作在评估 _await_expression * 时可能会或可能不会完成。It represents an asynchronous operation that may or may not be complete at the time the _await_expression* is evaluated. Await 运算符的用途是在等待的任务完成之前挂起封闭异步函数的执行,然后获取其结果。The purpose of the await operator is to suspend execution of the enclosing async function until the awaited task is complete, and then obtain its outcome.
可等待表达式Awaitable expressions
Await 表达式的任务需要 可等待。The task of an await expression is required to be awaitable. t
如果以下包含其中一项,则表达式为可等待:An expression t
is awaitable if one of the following holds:
t
属于编译时类型dynamic
t
is of compile time typedynamic
t
具有调用的可访问实例或扩展方法GetAwaiter
,其中不包含任何参数和类型参数,以及A
以下所有保留的返回类型:t
has an accessible instance or extension method calledGetAwaiter
with no parameters and no type parameters, and a return typeA
for which all of the following hold:A
实现接口System.Runtime.CompilerServices.INotifyCompletion
(以后称为的简称INotifyCompletion
)A
implements the interfaceSystem.Runtime.CompilerServices.INotifyCompletion
(hereafter known asINotifyCompletion
for brevity)A
具有类型的可访问的可读实例属性IsCompleted``bool
A
has an accessible, readable instance propertyIsCompleted
of typebool
A
具有一个GetResult
无参数且无类型参数的可访问实例方法A
has an accessible instance methodGetResult
with no parameters and no type parameters
此方法的目的 GetAwaiter
是获取任务的 *awaiter _。The purpose of the GetAwaiter
method is to obtain an *awaiter _ for the task. A
对于 await 表达式,类型称为 _ *awaiter type**。The type A
is called the _ awaiter type* for the await expression.
属性的用途 IsCompleted
是确定任务是否已完成。The purpose of the IsCompleted
property is to determine if the task is already complete. 如果是这样,则无需暂停评估。If so, there is no need to suspend evaluation.
此方法的目的 INotifyCompletion.OnCompleted
是将 "延续" 注册到任务,即) 类型的委托 (System.Action
,一旦任务完成,就会调用该委托。The purpose of the INotifyCompletion.OnCompleted
method is to sign up a "continuation" to the task; i.e. a delegate (of type System.Action
) that will be invoked once the task is complete.
此方法的目的 GetResult
是在任务完成后获取任务的结果。The purpose of the GetResult
method is to obtain the outcome of the task once it is complete. 此结果可能会成功完成(可能包含结果值),也可能是由方法引发的异常 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()
. 因此,如果的返回类型 GetResult
为 void
,则 await_expression 分类为 nothing。Thus, if the return type of GetResult
is void
, the await_expression is classified as nothing. 如果它具有非 void 返回类型 T
,则将 await_expression 分类为类型的值 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:
a
通过计算表达式来获取 awaiter(t).GetAwaiter()
。An awaitera
is obtained by evaluating the expression(t).GetAwaiter()
.bool
b
通过计算表达式获得(a).IsCompleted
。Abool
b
is obtained by evaluating the expression(a).IsCompleted
.- 如果
b
为,false
则计算结果取决于是否a
实现了接口System.Runtime.CompilerServices.ICriticalNotifyCompletion
(以后ICriticalNotifyCompletion
) 的简称。Ifb
isfalse
then evaluation depends on whethera
implements the interfaceSystem.Runtime.CompilerServices.ICriticalNotifyCompletion
(hereafter known asICriticalNotifyCompletion
for brevity). 此检查是在绑定时进行的;例如,在运行时a
,如果具有编译时类型,则为dynamic
; 否则为。This check is done at binding time; i.e. at runtime ifa
has the compile time typedynamic
, and at compile time otherwise. 让r
(异步函数) 恢复委托:Letr
denote the resumption delegate (Async functions):- 如果不
a
实现ICriticalNotifyCompletion
,则(a as (INotifyCompletion)).OnCompleted(r)
计算表达式。Ifa
does not implementICriticalNotifyCompletion
, then the expression(a as (INotifyCompletion)).OnCompleted(r)
is evaluated. - 如果
a
实现ICriticalNotifyCompletion
,则(a as (ICriticalNotifyCompletion)).UnsafeOnCompleted(r)
计算表达式。Ifa
does implementICriticalNotifyCompletion
, 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
true
) ,则 (立即后,或在以后调用恢复委托) (时b
,将false
计算该表达式(a).GetResult()
。Either immediately after (ifb
wastrue
), or upon later invocation of the resumption delegate (ifb
wasfalse
), the expression(a).GetResult()
is evaluated. 如果返回值,则该值是 await_expression 的结果。If it returns a value, that value is the result of the await_expression. 否则,结果为 nothing。Otherwise the result is nothing.
Awaiter 的接口方法实现 INotifyCompletion.OnCompleted
并 ICriticalNotifyCompletion.UnsafeOnCompleted
应导致委托 r
最多调用一次。An awaiter's implementation of the interface methods INotifyCompletion.OnCompleted
and ICriticalNotifyCompletion.UnsafeOnCompleted
should cause the delegate r
to be invoked at most once. 否则,封闭异步函数的行为是不确定的。Otherwise, the behavior of the enclosing async function is undefined.
算术运算符Arithmetic operators
*
、、 /
%
、 +
和 -
运算符称为算术运算符。The *
, /
, %
, +
, and -
operators are called the arithmetic operators.
multiplicative_expression
: unary_expression
| multiplicative_expression '*' unary_expression
| multiplicative_expression '/' unary_expression
| multiplicative_expression '%' unary_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
如果算术运算符的操作数具有编译时类型 dynamic
,则该表达式将动态绑定 (动态绑定) 。If an operand of an arithmetic operator has the compile-time type dynamic
, then the expression is dynamically bound (Dynamic binding). 在这种情况下,表达式的编译时类型为 dynamic
,并使用具有编译时类型的操作数的运行时类型在运行时执行下面所述的解决方案 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. 运算符都计算和的乘积 x
y
。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.OverflowException
则会引发。In achecked
context, if the product is outside the range of the result type, aSystem.OverflowException
is thrown. 在unchecked
上下文中,不会报告溢出,并且放弃结果类型范围外的任何重要高序位。In anunchecked
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. 在表中,
x
和y
为正有限值。In the table,x
andy
are positive finite values.x * y
的结果为z
。z
is the result ofx * 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.OverflowException
则会引发。If the resulting value is too large to represent in thedecimal
format, aSystem.OverflowException
is thrown. 如果结果值太小而无法以decimal
格式表示,则结果为零。If the result value is too small to represent in thedecimal
format, the result is zero. 在进行任何舍入之前,结果的小数位数是两个操作数的刻度之和。The scale of the result, before any rounding, is the sum of the scales of the two operands.Decimal 乘法等效于使用类型的乘法运算符
System.Decimal
。Decimal multiplication is equivalent to using the multiplication operator of typeSystem.Decimal
.
除法运算符Division operator
对于窗体操作 x / y
,二元运算符重载决策 (二元运算符重载决策 ,) 应用于选择特定的运算符实现。For an operation of the form x / y
, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型就是运算符的返回类型。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
下面列出了预定义的除法运算符。The predefined division operators are listed below. 运算符都计算和的商 x
y
。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.DivideByZeroException
则将引发。If the value of the right operand is zero, aSystem.DivideByZeroException
is thrown.相除将结果舍入到零。The division rounds the result towards zero. 因此,结果的绝对值是小于或等于两个操作数的商的绝对值的最大整数。Thus the absolute value of the result is the largest possible integer that is less than or equal to the absolute value of the quotient of the two operands. 如果两个操作数具有相同的符号,则结果为零或正; 如果两个操作数具有相反的符号,则结果为零或负。The result is zero or positive when the two operands have the same sign and zero or negative when the two operands have opposite signs.
如果左操作数是最小的可
int
long
表示或值并且右操作数为-1
,则发生溢出。If the left operand is the smallest representableint
orlong
value and the right operand is-1
, an overflow occurs. 在checked
上下文中,这将导致System.ArithmeticException
引发) (或子类。In achecked
context, this causes aSystem.ArithmeticException
(or a subclass thereof) to be thrown. 在unchecked
上下文中,它的实现定义为:是否引发了System.ArithmeticException
) 的 (或子类,或溢出与左操作数的结果值无报告。In anunchecked
context, it is implementation-defined as to whether aSystem.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. 在表中,
x
和y
为正有限值。In the table,x
andy
are positive finite values.x / y
的结果为z
。z
is the result ofx / y
. 如果结果相对于目标类型而言太大,则z
为无穷值。If the result is too large for the destination type,z
is infinity. 如果结果相对于目标类型而言太小,则z
为零。If the result is too small for the destination type,z
is zero.+y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN +x+x +z+z -Z-z +inf+inf -inf-inf +0+0 -0-0 NaNNaN -X-x -Z-z +z+z -inf-inf +inf+inf -0-0 +0+0 NaNNaN +0+0 +0+0 -0-0 NaNNaN NaNNaN +0+0 -0-0 NaNNaN -0-0 -0-0 +0+0 NaNNaN NaNNaN -0-0 +0+0 NaNNaN +inf+inf +inf+inf -inf-inf +inf+inf -inf-inf NaNNaN NaNNaN NaNNaN -inf-inf -inf-inf +inf+inf -inf-inf +inf+inf NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN 小数部分:Decimal division:
decimal operator /(decimal x, decimal y);
如果右操作数的值为零,
System.DivideByZeroException
则将引发。If the value of the right operand is zero, aSystem.DivideByZeroException
is thrown. 如果生成的值太大而无法以格式表示decimal
,System.OverflowException
则会引发。If the resulting value is too large to represent in thedecimal
format, aSystem.OverflowException
is thrown. 如果结果值太小而无法以decimal
格式表示,则结果为零。If the result value is too small to represent in thedecimal
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 typeSystem.Decimal
.
余数运算符Remainder operator
对于窗体操作 x % y
,二元运算符重载决策 (二元运算符重载决策 ,) 应用于选择特定的运算符实现。For an operation of the form x % y
, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型就是运算符的返回类型。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
下面列出了预定义的余数运算符。The predefined remainder operators are listed below. 运算符都计算和之间相除的余数 x
y
。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);
的结果
x % y
是生成的值x - (x / y) * y
。The result ofx % y
is the value produced byx - (x / y) * y
. 如果y
为零,将System.DivideByZeroException
引发。Ify
is zero, aSystem.DivideByZeroException
is thrown.如果左操作数是最小
int
值或long
值并且右操作数为-1
,则System.OverflowException
引发。If the left operand is the smallestint
orlong
value and the right operand is-1
, aSystem.OverflowException
is thrown. 在任何情况下,都不会引发异常x % y
,x / y
而不会引发异常。In no case doesx % y
throw an exception wherex / 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. 在表中,
x
和y
为正有限值。In the table,x
andy
are positive finite values.z
和的结果x % y
计算为x - n * y
,其中n
是小于或等于的最大整数x / y
。z
is the result ofx % y
and is computed asx - n * y
, wheren
is the largest possible integer that is less than or equal tox / y
. 计算余数的这种方法类似于用于整数操作数,但不同于 IEEE 754 定义 (,这n
是最接近) 的整数x / y
。This method of computing the remainder is analogous to that used for integer operands, but differs from the IEEE 754 definition (in whichn
is the integer closest tox / y
).+y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN +x+x +z+z +z+z NaNNaN NaNNaN xx xx NaNNaN -X-x -Z-z -Z-z NaNNaN NaNNaN -X-x -X-x NaNNaN +0+0 +0+0 +0+0 NaNNaN NaNNaN +0+0 +0+0 NaNNaN -0-0 -0-0 -0-0 NaNNaN NaNNaN -0-0 -0-0 NaNNaN +inf+inf NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN -inf-inf NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN 小数余数:Decimal remainder:
decimal operator %(decimal x, decimal y);
如果右操作数的值为零,
System.DivideByZeroException
则将引发。If the value of the right operand is zero, aSystem.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 ofx
.小数余数等效于使用类型的余数运算符
System.Decimal
。Decimal remainder is equivalent to using the remainder operator of typeSystem.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.OverflowException
则会引发。In achecked
context, if the sum is outside the range of the result type, aSystem.OverflowException
is thrown. 在unchecked
上下文中,不会报告溢出,并且放弃结果类型范围外的任何重要高序位。In anunchecked
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. 在表中,
x
和y
是非零有限值,z
是x + y
的结果。In the table,x
andy
are nonzero finite values, andz
is the result ofx + y
. 如果x
和y
度量值相同但异号,则z
为正零。Ifx
andy
have the same magnitude but opposite signs,z
is positive zero. 如果x + y
太大而无法在目标类型中表示,z
则是一个无穷大,其符号与相同x + y
。Ifx + y
is too large to represent in the destination type,z
is an infinity with the same sign asx + 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.OverflowException
则会引发。If the resulting value is too large to represent in thedecimal
format, aSystem.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 typeSystem.Decimal
.枚举加法。Enumeration addition. 每个枚举类型都隐式提供以下预定义运算符,其中
E
是枚举类型,U
是的基础类型E
:Every enumeration type implicitly provides the following predefined operators, whereE
is the enum type, andU
is the underlying type ofE
:E operator +(E x, U y); E operator +(U x, E y);
在运行时,这些运算符的计算结果完全相同
(E)((U)x + (U)y)
。At run-time these operators are evaluated exactly as(E)((U)x + (U)y)
.字符串串联:String concatenation:
string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y);
二元运算符的这些重载
+
执行字符串串联。These overloads of the binary+
operator perform string concatenation. 如果字符串串联的操作数为null
,则将替换空字符串。If an operand of string concatenation isnull
, an empty string is substituted. 否则,通过调用从类型继承的虚拟方法,将任何非字符串参数转换为其字符串表示形式ToString
object
。Otherwise, any non-string argument is converted to its string representation by invoking the virtualToString
method inherited from typeobject
. 如果ToString
返回null
,则替换空字符串。IfToString
returnsnull
, an empty string is substituted.using System; class Test { static void Main() { string s = null; Console.WriteLine("s = >" + s + "<"); // displays s = >< int i = 1; Console.WriteLine("i = " + i); // displays i = 1 float f = 1.2300E+15F; Console.WriteLine("f = " + f); // displays f = 1.23E+15 decimal d = 2.900m; Console.WriteLine("d = " + d); // displays d = 2.900 } }
字符串串联运算符的结果是一个字符串,该字符串由左操作数的字符后跟右操作数的字符组成。 字符串串联运算符从不返回
null
值。System.OutOfMemoryException
如果没有足够的内存可用于分配生成的字符串,可能会引发。ASystem.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, whereD
is the delegate type:D operator +(D x, D y);
二元
+
运算符在两个操作数均为某个委托类型时执行委托组合D
。The binary+
operator performs delegate combination when both operands are of some delegate typeD
. (如果操作数具有不同的委托类型,则发生绑定时错误 ) 。如果第一个操作数为null
,则操作的结果为第二个操作数的值 (即使还) 也是如此null
。(If the operands have different delegate types, a binding-time error occurs.) If the first operand isnull
, the result of the operation is the value of the second operand (even if that is alsonull
). 否则,如果第二个操作数为null
,则运算结果为第一个操作数的值。Otherwise, if the second operand isnull
, then the result of the operation is the value of the first operand. 否则,该操作的结果是一个新的委托实例,它在调用时调用第一个操作数,然后调用第二个操作数。Otherwise, the result of the operation is a new delegate instance that, when invoked, invokes the first operand and then invokes the second operand. 有关委托组合的示例,请参阅 减法运算符 和 委托调用。For examples of delegate combination, see Subtraction operator and Delegate invocation. 由于不是System.Delegate
委托类型,operator
+
因此不会为其定义。SinceSystem.Delegate
is not a delegate type,operator
+
is not defined for it.
减法运算符Subtraction operator
对于窗体操作 x - y
,二元运算符重载决策 (二元运算符重载决策 ,) 应用于选择特定的运算符实现。For an operation of the form x - y
, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型就是运算符的返回类型。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
下面列出了预定义的减法运算符。The predefined subtraction operators are listed below. 运算符均 y
从中减去 x
。The operators all subtract y
from x
.
整数减法:Integer subtraction:
int operator -(int x, int y); uint operator -(uint x, uint y); long operator -(long x, long y); ulong operator -(ulong x, ulong y);
在
checked
上下文中,如果差异超出了结果类型的范围,System.OverflowException
则会引发。In achecked
context, if the difference is outside the range of the result type, aSystem.OverflowException
is thrown. 在unchecked
上下文中,不会报告溢出,并且放弃结果类型范围外的任何重要高序位。In anunchecked
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. 在表中,
x
和y
是非零有限值,z
是x - y
的结果。In the table,x
andy
are nonzero finite values, andz
is the result ofx - y
. 如果x
和y
相等,则z
为正零。Ifx
andy
are equal,z
is positive zero. 如果x - y
太大而无法在目标类型中表示,z
则是一个无穷大,其符号与相同x - y
。Ifx - y
is too large to represent in the destination type,z
is an infinity with the same sign asx - 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.OverflowException
则会引发。If the resulting value is too large to represent in thedecimal
format, aSystem.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 typeSystem.Decimal
.枚举减法。Enumeration subtraction. 每个枚举类型都隐式提供以下预定义运算符,其中
E
是枚举类型,U
是的基础类型E
:Every enumeration type implicitly provides the following predefined operator, whereE
is the enum type, andU
is the underlying type ofE
:U operator -(E x, E y);
此运算符的计算结果完全相同
(U)((U)x - (U)y)
。This operator is evaluated exactly as(U)((U)x - (U)y)
. 换言之,运算符计算和的序号值之间的差x
y
,结果的类型为枚举的基础类型。In other words, the operator computes the difference between the ordinal values ofx
andy
, 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, whereD
is the delegate type:D operator -(D x, D y);
-
当两个操作数均为某个委托类型时,二元运算符执行委托删除D
。The binary-
operator performs delegate removal when both operands are of some delegate typeD
. 如果操作数具有不同的委托类型,则会发生绑定时错误。If the operands have different delegate types, a binding-time error occurs. 如果第一个操作数为null
,则操作结果为null
。If the first operand isnull
, the result of the operation isnull
. 否则,如果第二个操作数为null
,则运算结果为第一个操作数的值。Otherwise, if the second operand isnull
, 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. 如果删除行为导致出现空列表,则结果为null
。If removal results in an empty list, the result isnull
. 例如:For example:delegate void D(int x); class C { public static void M1(int i) { /* ... */ } public static void M2(int i) { /* ... */ } } class Test { static void Main() { D cd1 = new D(C.M1); D cd2 = new D(C.M2); D cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1 cd3 -= cd1; // => M1 + M2 + M2 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1 cd3 -= cd1 + cd2; // => M2 + M1 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1 cd3 -= cd2 + cd2; // => M1 + M1 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1 cd3 -= cd2 + cd1; // => M1 + M2 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1 cd3 -= cd1 + cd1; // => M1 + M2 + M2 + M1 } }
移位运算符Shift operators
<<
和 >>
运算符用于执行移位运算。The <<
and >>
operators are used to perform bit shifting operations.
shift_expression
: additive_expression
| shift_expression '<<' additive_expression
| shift_expression right_shift additive_expression
;
如果 shift_expression 的操作数具有编译时类型 dynamic
,则表达式 (动态 绑定) 动态绑定。If an operand of a shift_expression has the compile-time type dynamic
, then the expression is dynamically bound (Dynamic binding). 在这种情况下,表达式的编译时类型为 dynamic
,并使用具有编译时类型的操作数的运行时类型在运行时执行下面所述的解决方案 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 << count
x >> count
,二元运算符重载决策 (二元运算符重载解析) 应用于选择特定的运算符实现。For an operation of the form x << count
or x >> count
, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型就是运算符的返回类型。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
在声明重载移位运算符时,第一个操作数的类型必须始终为包含运算符声明的类或结构,并且第二个操作数的类型必须始终为 int
。When declaring an overloaded shift operator, the type of the first operand must always be the class or struct containing the operator declaration, and the type of the second operand must always be int
.
下面列出了预定义的移位运算符。The predefined shift operators are listed below.
左移:Shift left:
int operator <<(int x, int count); uint operator <<(uint x, int count); long operator <<(long x, int count); ulong operator <<(ulong x, int count);
<<
运算符x
向下移位按下面所述计算的位数。The<<
operator shiftsx
left by a number of bits computed as described below.将丢弃结果类型范围外的高序位
x
,剩余位将向左移动,并将低序位空位位置设置为零。The high-order bits outside the range of the result type ofx
are discarded, the remaining bits are shifted left, and the low-order empty bit positions are set to zero.右移:Shift right:
int operator >>(int x, int count); uint operator >>(uint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count);
>>
运算符x
向右移位按如下所述计算的位数。The>>
operator shiftsx
right by a number of bits computed as described below.当
x
的类型为int
或时long
,将丢弃的低序位,x
其余位将向右移动,如果为非负,则将高顺序空位位置设置为零; 如果为负,则设置x
为 1x
。Whenx
is of typeint
orlong
, the low-order bits ofx
are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero ifx
is non-negative and set to one ifx
is negative.当
x
的类型为uint
或时ulong
,将丢弃的低序位,x
其余位将向右移动,而高阶空位位置设置为零。Whenx
is of typeuint
orulong
, the low-order bits ofx
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:
- 当的类型
x
为int
或时uint
,移位计数由的低序位5位给定count
。When the type ofx
isint
oruint
, the shift count is given by the low-order five bits ofcount
. 换句话说,移位计数是从计算得出的count & 0x1F
。In other words, the shift count is computed fromcount & 0x1F
. - 当的类型
x
为long
或时ulong
,移位计数由的低序位六位给定count
。When the type ofx
islong
orulong
, the shift count is given by the low-order six bits ofcount
. 换句话说,移位计数是从计算得出的count & 0x3F
。In other words, the shift count is computed fromcount & 0x3F
.
如果生成的移位计数为零,则移位运算符只返回的值 x
。If the resulting shift count is zero, the shift operators simply return the value of x
.
移位运算从不导致溢出并在和上下文中产生相同的结果 checked
unchecked
。Shift operations never cause overflows and produce the same results in checked
and unchecked
contexts.
当运算符的左操作数 >>
为有符号整数类型时,运算符将执行算术右移,其中最高有效位的值 (操作数的符号位) 传播到高顺序空位位置。When the left operand of the >>
operator is of a signed integral type, the operator performs an arithmetic shift right wherein the value of the most significant bit (the sign bit) of the operand is propagated to the high-order empty bit positions. 当运算符的左操作数 >>
是无符号整数类型时,运算符将执行逻辑移位,其中高阶空位位置始终设置为零。When the left operand of the >>
operator is of an unsigned integral type, the operator performs a logical shift right wherein high-order empty bit positions are always set to zero. 若要执行从操作数类型推断得出的相反运算,可以使用显式强制转换。To perform the opposite operation of that inferred from the operand type, explicit casts can be used. 例如,如果 x
是类型为的变量 int
,则该操作将 unchecked((int)((uint)x >> y))
执行的逻辑移位 x
。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
==
、、 !=
<
、 >
、 <=
、 >=
is
和 as
运算符称为关系和类型测试运算符。The ==
, !=
, <
, >
, <=
, >=
, is
and as
operators are called the relational and type-testing operators.
relational_expression
: shift_expression
| relational_expression '<' shift_expression
| relational_expression '>' shift_expression
| relational_expression '<=' shift_expression
| relational_expression '>=' shift_expression
| relational_expression 'is' type
| relational_expression 'as' type
;
equality_expression
: relational_expression
| equality_expression '==' relational_expression
| equality_expression '!=' relational_expression
;
is
运算符在is 运算符中进行了描述, as
运算符在as 运算符中进行了描述。The is
operator is described in The is operator and the as
operator is described in The as operator.
==
、、 !=
<
、 >
<=
和 >=
运算符是 比较运算符。The ==
, !=
, <
, >
, <=
and >=
operators are comparison operators.
如果比较运算符的操作数具有编译时类型 dynamic
,则该表达式将动态绑定 (动态绑定) 。If an operand of a comparison operator has the compile-time type dynamic
, then the expression is dynamically bound (Dynamic binding). 在这种情况下,表达式的编译时类型为 dynamic
,并使用具有编译时类型的操作数的运行时类型在运行时执行下面所述的解决方案 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
.
对于 op 格式的操作 x
y
,其中 op 是比较运算符, (二元运算符重载决策的重载决策) 应用于选择特定的运算符实现。For an operation of the form x
op y
, where op is a comparison operator, overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型就是运算符的返回类型。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
以下各节介绍了预定义的比较运算符。The predefined comparison operators are described in the following sections. 所有预定义的比较运算符都返回一个类型的结果 bool
,如下表所述。All predefined comparison operators return a result of type bool
, as described in the following table.
操作Operation | 结果Result |
---|---|
x == y |
true 如果 x 等于,则 y 为 false ; 否则为true if x is equal to y , false otherwise |
x != y |
true 如果 x 不等于,则 y 为 false ; 否则为true if x is not equal to y , false otherwise |
x < y |
如果 x 小于 y ,则为 true ;否则为 false true if x is less than y , false otherwise |
x > y |
如果 x 大于 y ,则为 true ;否则为 false true if x is greater than y , false otherwise |
x <= y |
如果 x 小于等于 y ,则为 true ;否则为 false true if x is less than or equal to y , false otherwise |
x >= y |
如果 x 大于等于 y ,则为 true ;否则为 false true 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
值,该值指示特定关系是 true
还是 false
。Each 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
除之外的所有运算符!=
true
。If either operand is NaN, the result isfalse
for all operators except!=
, for which the result istrue
. 对于任意两个操作数,x != y
始终产生与相同的结果!(x == y)
。For any two operands,x != y
always produces the same result as!(x == y)
. 但是,当一个或两个操作数为 NaN 时,<
、>
、<=
和>=
运算符不会生成与相反运算符的逻辑求反的结果。However, when one or both operands are NaN, the<
,>
,<=
, and>=
operators do not produce the same results as the logical negation of the opposite operator. 例如,如果x
和y
均为 NaN,则x < y
为false
,而为!(x >= y)
true
。For example, if either ofx
andy
is NaN, thenx < y
isfalse
, but!(x >= y)
istrue
.当两个操作数均为 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
其中
min
,和max
是可采用给定浮点格式表示的最小和最大正有限值。wheremin
andmax
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
值,该值指示特定关系是 true
还是 false
。Each 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);
如果和均为,则的结果 ==
为 true
x
y
true
或 x
y
false
。The result of ==
is true
if both x
and y
are true
or if both x
and y
are false
. 否则,结果为 false
。Otherwise, the result is false
.
如果和均为,则的结果 !=
为 false
x
y
true
或 x
y
false
。The result of !=
is false
if both x
and y
are true
or if both x
and y
are false
. 否则,结果为 true
。Otherwise, the result is true
. 当操作数为类型时 bool
, !=
运算符产生与运算符相同的结果 ^
。When the operands are of type bool
, the !=
operator produces the same result as the ^
operator.
枚举比较运算符Enumeration comparison operators
每个枚举类型都隐式提供以下预定义的比较运算符:Every enumeration type implicitly provides the following predefined comparison operators:
bool operator ==(E x, E y);
bool operator !=(E x, E y);
bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);
计算结果 x op y
,其中 x
和 y
是具有基础类型的枚举类型的表达式 E
U
, 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
,因此它们适用于不声明适用的和成员的所有类型 operator ==
operator !=
。Since the predefined reference type equality operators accept operands of type object
, they apply to all types that do not declare applicable operator ==
and operator !=
members. 相反,任何适用的用户定义的相等运算符都可以有效地隐藏预定义的引用类型相等运算符。Conversely, any applicable user-defined equality operators effectively hide the predefined reference type equality operators.
预定义的引用类型相等运算符需要以下项之一:The predefined reference type equality operators require one of the following:
- 这两个操作数都是已知为 reference_type 或文本的类型的值
null
。Both operands are a value of a type known to be a reference_type or the literalnull
. 此外,显式引用转换 (显式 引用 转换) 从任一操作数的类型到另一个操作数的类型。Furthermore, an explicit reference conversion (Explicit reference conversions) exists from the type of either operand to the type of the other operand. - 一个操作数是类型的值,
T
其中T
是 type_parameter ,另一个操作数是文本null
。One operand is a value of typeT
whereT
is a type_parameter and the other operand is the literalnull
. 此外T
,也没有值类型约束。FurthermoreT
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. 例如,如果操作数的绑定时类型是两个类类型,并且
A
B
,如果两者都不是A
B
派生的,则两个操作数不可能引用同一个对象。For example, if the binding-time types of the operands are two class typesA
andB
, and if neitherA
norB
derives from the other, then it would be impossible for the two operands to reference the same object. 因此,操作被视为绑定时错误。Thus, the operation is considered a binding-time error. - 预定义的引用类型相等运算符不允许比较值类型操作数。The predefined reference type equality operators do not permit value type operands to be compared. 因此,除非结构类型声明自己的相等运算符,否则不能比较该结构类型的值。Therefore, unless a struct type declares its own equality operators, it is not possible to compare values of that struct type.
- 预定义的引用类型相等运算符永远不会导致其操作数发生装箱操作。The predefined reference type equality operators never cause boxing operations to occur for their operands. 执行此类装箱操作会毫无意义,因为对新分配的装箱实例的引用必须与所有其他引用不同。It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references.
- 如果将类型参数类型的操作数
T
与进行比较null
,并且的运行时类型T
为值类型,则比较结果为false
。If an operand of a type parameter typeT
is compared tonull
, and the run-time type ofT
is a value type, the result of the comparison isfalse
.
下面的示例检查不受约束的类型参数类型的参数是否为 null
。The following example checks whether an argument of an unconstrained type parameter type is null
.
class C<T>
{
void F(T x) {
if (x == null) throw new ArgumentNullException();
...
}
}
x == null
即使 T
可能表示值类型,也允许构造,并且仅将结果定义为 false
T
值类型。The x == null
construct is permitted even though T
could represent a value type, and the result is simply defined to be false
when T
is a value type.
对于窗体的操作 x == y
x != y
(如果有任何适用 operator ==
或 operator !=
存在),运算符重载决策 (二元运算符重载解析) 规则将选择该运算符而不是预定义的引用类型相等运算符。For an operation of the form x == y
or x != y
, if any applicable operator ==
or operator !=
exists, the operator overload resolution (Binary operator overload resolution) rules will select that operator instead of the predefined reference type equality operator. 但是,始终可以通过将一个或两个操作数显式强制转换为类型来选择预定义的引用类型相等运算符 object
。However, 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
s
和 t
变量引用 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
由于在一个或两个操作数为类型时选择了预定义引用类型相等运算符,因此剩余的比较比较 object
。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
因为强制转换会创建对两个单独的装箱值实例的引用 int
。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:
- 这两个值都是
null
。Both values arenull
. - 对于在每个字符位置都具有相同长度和相同字符的字符串实例,这两个值都是非空引用。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
,则当且仅当两者都为时,它们才相等null
。If either of the delegate instances isnull
, they are equal if and only if both arenull
. - 如果委托具有不同的运行时类型,则它们永远不相等。If the delegates have different run-time type they are never equal.
- 如果两个委托实例都 (委托声明) 调用列表,则当且仅当其调用列表相同时,这些实例相等,并且一个调用列表中的每个条目都相等 (如下所示) 到其他的调用列表中的相应条目。If both of the delegate instances have an invocation list (Delegate declarations), those instances are equal if and only if their invocation lists are the same length, and each entry in one's invocation list is equal (as defined below) to the corresponding entry, in order, in the other's invocation list.
以下规则控制调用列表项的相等性:The following rules govern the equality of invocation list entries:
- 如果两个调用列表项引用相同的静态方法,则这些项相等。If two invocation list entries both refer to the same static method then the entries are equal.
- 如果两个调用列表项引用相同的目标对象上与引用相等运算符定义 (相同的非静态方法) 则这些项相等。If two invocation list entries both refer to the same non-static method on the same target object (as defined by the reference equality operators) then the entries are equal.
- 从语义上完全相同的 anonymous_method_expression s 或 lambda_expression 的计算生成的调用列表条目具有相同的 (可能为空) 允许的一组捕获的外部变量实例 (但不要求) 相等。Invocation list entries produced from evaluation of semantically identical anonymous_method_expression s or lambda_expression s with the same (possibly empty) set of captured outer variable instances are permitted (but not required) to be equal.
相等运算符和 nullEquality operators and null
==
和 !=
运算符允许一个操作数为可以为 null 的类型的值,另一个操作数为 null
原义值,即使操作中没有预定义或用户定义的运算符 (unlifted 或提升形式) 。The ==
and !=
operators permit one operand to be a value of a nullable type and the other to be the null
literal, even if no predefined or user-defined operator (in unlifted or lifted form) exists for the operation.
对于某个窗体的操作For an operation of one of the forms
x == null
null == x
x != null
null != x
其中 x
是可以为 null 的类型的表达式,如果运算符重载决策 (二元运算符重载决策) 未能找到适用的运算符,则结果是从的属性计算而来的 HasValue
x
。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.HasValue
。Specifically, the first two forms are translated into !x.HasValue
, and last two forms are translated into x.HasValue
.
is 运算符The is operator
is
运算符用于动态检查对象的运行时类型与给定类型是否兼容。The is
operator is used to dynamically check if the run-time type of an object is compatible with a given type. 操作的结果 E is T
(其中是一个 E
表达式, T
是一个类型),它是一个布尔值,指示是否 E
可以 T
通过引用转换、装箱转换或取消装箱转换成功转换为类型。The result of the operation E is T
, where E
is an expression and T
is a type, is a boolean value indicating whether E
can successfully be converted to type T
by a reference conversion, a boxing conversion, or an unboxing conversion. 在将类型参数替换为所有类型参数后,按如下所示计算运算:The operation is evaluated as follows, after type arguments have been substituted for all type parameters:
- 如果
E
是匿名函数,则会发生编译时错误。IfE
is an anonymous function, a compile-time error occurs - 如果
E
是方法组或null
文本,则如果的类型E
是引用类型或可以为 null 的类型并且的值E
为 null,则结果为 false。IfE
is a method group or thenull
literal, of if the type ofE
is a reference type or a nullable type and the value ofE
is null, the result is false. - 否则,让我们按如下所示
D
表示动态类型E
:Otherwise, letD
represent the dynamic type ofE
as follows:- 如果的类型
E
为引用类型,D
则是实例引用的运行时类型E
。If the type ofE
is a reference type,D
is the run-time type of the instance reference byE
. - 如果的类型
E
是可以为 null 的类型,D
则为该可为 null 的类型的基础类型。If the type ofE
is a nullable type,D
is the underlying type of that nullable type. - 如果的类型
E
是不可以为 null 的值类型,D
则是的类型E
。If the type ofE
is a non-nullable value type,D
is the type ofE
.
- 如果的类型
- 操作的结果取决于
D
和,如下所示T
:The result of the operation depends onD
andT
as follows:- 如果
T
是引用类型,并且如果是D
T
D
引用类型且从到的隐式引用转换为,则结果为 true;D
T
如果D
是值类型,并且从到的装箱转换D
为T
,则结果为 true。IfT
is a reference type, the result is true ifD
andT
are the same type, ifD
is a reference type and an implicit reference conversion fromD
toT
exists, or ifD
is a value type and a boxing conversion fromD
toT
exists. - 如果
T
是可以为 null 的类型,并且是的基础类型,则结果为 trueD
T
。IfT
is a nullable type, the result is true ifD
is the underlying type ofT
. - 如果
T
是不可以为 null 的值类型,则如果D
和T
是相同类型,则结果为 true。IfT
is a non-nullable value type, the result is true ifD
andT
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
as
运算符用于将值显式转换为给定引用类型或可以为 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. 相反,如果不可能指定的转换,则生成的值为 null
。Instead, 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:
- 标识 (标识转换) ,隐式可为 Null (隐式可为 null 的转换) ,隐式引用 (隐式引用转换) ,装箱 (装箱转换) ,显式引用 (显式引用转换) 或取消装箱 (从到的转换) 转换
E
T
。An identity (Identity conversion), implicit nullable (Implicit nullable conversions), implicit reference (Implicit reference conversions), boxing (Boxing conversions), explicit nullable (Explicit nullable conversions), explicit reference (Explicit reference conversions), or unboxing (Unboxing conversions) conversion exists fromE
toT
. - 或的类型
E
T
为开放类型。The type ofE
orT
is an open type. E``null
文本。E
is thenull
literal.
如果的编译时类型 E
不是 dynamic
,则该操作 E as T
生成的结果与If the compile-time type of E
is not dynamic
, the operation E as T
produces the same result as
E is T ? (T)(E) : (T)null
不同的是 E
只计算一次。except that E
is only evaluated once. 编译器可以优化 E as T
到最多执行一次动态类型检查,而不是上述扩展隐含的两种动态类型检查。The compiler can be expected to optimize E as T
to perform at most one dynamic type check as opposed to the two dynamic type checks implied by the expansion above.
如果的编译时类型 E
为,则 dynamic
与强制转换运算符不同, as
运算符不 (动态绑定) 动态绑定。If the compile-time type of E
is dynamic
, unlike the cast operator the as
operator is not dynamically bound (Dynamic binding). 因此,在这种情况下,展开是:Therefore the expansion in this case is:
E is T ? (T)(object)(E) : (T)null
请注意,某些转换(如用户定义的转换)无法与运算符一起使用, as
应改为使用强制转换表达式执行。Note that some conversions, such as user defined conversions, are not possible with the as
operator and should instead be performed using cast expressions.
示例中In the example
class X
{
public string F(object o) {
return o as string; // OK, string is a reference type
}
public T G<T>(object o) where T: Attribute {
return o as T; // Ok, T has a class constraint
}
public U H<U>(object o) {
return o as U; // Error, U is unconstrained
}
}
已知的类型 T
参数 G
是引用类型,因为它具有类约束。the type parameter T
of G
is known to be a reference type, because it has the class constraint. 但不是的类型参数, U
H
因此不 as
允许在中使用运算符 H
。The type parameter U
of H
is not however; hence the use of the as
operator in H
is disallowed.
逻辑运算符Logical operators
&
、 ^
和 |
运算符称为逻辑运算符。The &
, ^
, and |
operators are called the logical operators.
and_expression
: equality_expression
| and_expression '&' equality_expression
;
exclusive_or_expression
: and_expression
| exclusive_or_expression '^' and_expression
;
inclusive_or_expression
: exclusive_or_expression
| inclusive_or_expression '|' exclusive_or_expression
;
如果逻辑运算符的操作数具有编译时类型 dynamic
,则该表达式将动态绑定 (动态绑定) 。If an operand of a logical operator has the compile-time type dynamic
, then the expression is dynamically bound (Dynamic binding). 在这种情况下,表达式的编译时类型为 dynamic
,并使用具有编译时类型的操作数的运行时类型在运行时执行下面所述的解决方案 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 one of the logical operators, overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. 操作数将转换为所选运算符的参数类型,结果的类型就是运算符的返回类型。The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
以下各节介绍了预定义的逻辑运算符。The predefined logical operators are described in the following sections.
整数逻辑运算符Integer logical operators
预定义的整数逻辑运算符为:The predefined integer logical operators are:
int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);
int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);
int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);
&
运算符计算两个操作数的按位逻辑 AND
, |
运算符计算两个操作数的按位逻辑 OR
, ^
运算符计算 OR
两个操作数的按位逻辑异。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
(其中 x
和 y
是具有基础类型的枚举类型的表达式 E
U
), op
是一个逻辑运算符,与计算完全相同 (E)((U)x op (U)y)
。The result of evaluating x op y
, where x
and y
are expressions of an enumeration type E
with an underlying type U
, and op
is one of the logical operators, is exactly the same as evaluating (E)((U)x op (U)y)
. 换言之,枚举类型逻辑运算符只对两个操作数的基础类型执行逻辑运算。In other words, the enumeration type logical operators simply perform the logical operation on the underlying type of the two operands.
Boolean 逻辑运算符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);
如果 x
和 y
都为 true
,则 x & y
的结果为 true
。The result of x & y
is true
if both x
and y
are true
. 否则,结果为 false
。Otherwise, the result is false
.
x | y
true
如果 x
或 y
为,则的结果为 true
。The result of x | y
is true
if either x
or y
is true
. 否则,结果为 false
。Otherwise, the result is false
.
的结果 x ^ y
是 true
,如果 x
为 true
,并且为 y
false
,或 x
为 false
且 y
为 true
。The result of x ^ y
is true
if x
is true
and y
is false
, or x
is false
and y
is true
. 否则,结果为 false
。Otherwise, the result is false
. 当操作数为类型时 bool
,运算符将 ^
计算与运算符相同的结果 !=
。When the operands are of type bool
, the ^
operator computes the same result as the !=
operator.
可以为 null 的布尔逻辑运算符Nullable boolean logical operators
可以为 null 的布尔类型 bool?
可以表示三个值: true
、 false
和 null
,在概念上类似于用于 SQL 中的布尔表达式的三值类型。The nullable boolean type bool?
can represent three values, true
, false
, and null
, and is conceptually similar to the three-valued type used for boolean expressions in SQL. 为了确保操作数和运算符生成的结果 &
|
bool?
与 SQL 的三值逻辑一致,提供了以下预定义运算符:To ensure that the results produced by the &
and |
operators for bool?
operands are consistent with SQL's three-valued logic, the following predefined operators are provided:
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
下表列出了这些运算符为所有值、和组合所产生的结果 true
false
null
。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
&&
和 ||
运算符称为条件逻辑运算符。The &&
and ||
operators are called the conditional logical operators. 它们也称为 "短路" 逻辑运算符。They are also called the "short-circuiting" logical operators.
conditional_and_expression
: inclusive_or_expression
| conditional_and_expression '&&' inclusive_or_expression
;
conditional_or_expression
: conditional_and_expression
| conditional_or_expression '||' conditional_and_expression
;
&&
和 ||
运算符是和运算符的条件版本 &
|
:The &&
and ||
operators are conditional versions of the &
and |
operators:
- 操作
x && y
对应于操作,只是在不是时x & y
计算的除外y
x
false
。The operationx && y
corresponds to the operationx & y
, except thaty
is evaluated only ifx
is notfalse
. - 操作
x || y
对应于操作,只是在不是时x | y
计算的除外y
x
true
。The operationx || y
corresponds to the operationx | y
, except thaty
is evaluated only ifx
is nottrue
.
如果条件逻辑运算符的操作数具有编译时类型 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 && y
x || y
通过 (二元运算符重载解析) 应用重载决策来处理窗体或的操作,就像编写操作的方法 x & y
一样 x | y
。An operation of the form x && y
or x || y
is processed by applying overload resolution (Binary operator overload resolution) as if the operation was written x & y
or x | y
. 那么:Then,
- 如果重载决策未能找到单个最佳运算符,或者重载决策选择预定义的整数逻辑运算符之一,则会发生绑定时错误。If overload resolution fails to find a single best operator, or if overload resolution selects one of the predefined integer logical operators, a binding-time error occurs.
- 否则,如果所选运算符是预定义的布尔逻辑运算符之一 (布尔逻辑运算符) 或可以为 null 的布尔逻辑运算符) (可以为 null 的布尔 逻辑运算符,则按照 布尔条件逻辑运算符中所述处理操作。Otherwise, if the selected operator is one of the predefined boolean logical operators (Boolean logical operators) or nullable boolean logical operators (Nullable boolean logical operators), the operation is processed as described in Boolean conditional logical operators.
- 否则,所选运算符是用户定义的运算符,并且按照 用户定义的条件逻辑运算符中的描述处理操作。Otherwise, the selected operator is a user-defined operator, and the operation is processed as described in User-defined conditional logical operators.
不能直接重载条件逻辑运算符。It is not possible to directly overload the conditional logical operators. 但是,因为条件逻辑运算符是按常规逻辑运算符计算的,所以常规逻辑运算符的重载在某些限制条件下也被视为条件逻辑运算符的重载。However, because the conditional logical operators are evaluated in terms of the regular logical operators, overloads of the regular logical operators are, with certain restrictions, also considered overloads of the conditional logical operators. 用户定义的条件逻辑运算符中对此进行了进一步说明。This is described further in User-defined conditional logical operators.
布尔条件逻辑运算符Boolean conditional logical operators
当或的操作数 &&
为 ||
类型时 bool
,或者当操作数的类型未定义适用的 operator &
或 operator |
但确实定义了到的隐式转换时, bool
将按如下所示处理操作:When the operands of &&
or ||
are of type bool
, or when the operands are of types that do not define an applicable operator &
or operator |
, but do define implicit conversions to bool
, the operation is processed as follows:
- 运算的
x && y
计算结果为x ? y : false
。The operationx && y
is evaluated asx ? y : false
. 换言之,x
首先计算并转换为类型bool
。In other words,x
is first evaluated and converted to typebool
. 然后,如果x
为true
,y
则计算并转换为类型bool
,这将成为运算的结果。Then, ifx
istrue
,y
is evaluated and converted to typebool
, and this becomes the result of the operation. 否则,操作的结果为false
。Otherwise, the result of the operation isfalse
. - 运算的
x || y
计算结果为x ? true : y
。The operationx || y
is evaluated asx ? true : y
. 换言之,x
首先计算并转换为类型bool
。In other words,x
is first evaluated and converted to typebool
. 然后,如果x
为true
,则操作的结果为true
。Then, ifx
istrue
, the result of the operation istrue
. 否则,y
将计算并转换为类型bool
,这将成为运算的结果。Otherwise,y
is evaluated and converted to typebool
, and this becomes the result of the operation.
用户定义的条件逻辑运算符User-defined conditional logical operators
如果或的操作数 &&
属于 ||
声明适用的用户定义的或的类型 operator &
,则 operator |
以下两个条件都必须为 true,其中 T
是声明所选运算符的类型:When the operands of &&
or ||
are of types that declare an applicable user-defined operator &
or operator |
, both of the following must be true, where T
is the type in which the selected operator is declared:
- 所选运算符的每个参数的返回类型和类型必须为
T
。The return type and the type of each parameter of the selected operator must beT
. 换言之,运算符必须计算AND
OR
类型为的两个操作数的逻辑或逻辑T
,并且必须返回类型的结果T
。In other words, the operator must compute the logicalAND
or the logicalOR
of two operands of typeT
, and must return a result of typeT
. T
必须包含和的operator true
声明operator false
。T
must contain declarations ofoperator true
andoperator false
.
如果不满足上述任一要求,则会发生绑定时错误。A binding-time error occurs if either of these requirements is not satisfied. 否则, &&
通过将 ||
用户定义的 operator true
或 operator false
与选定的用户定义运算符相结合来计算或运算:Otherwise, the &&
or ||
operation is evaluated by combining the user-defined operator true
or operator false
with the selected user-defined operator:
- 运算的
x && y
计算结果为T.false(x) ? x : T.&(x, y)
,其中T.false(x)
是中声明的的调用operator false
T
,并且是对T.&(x, y)
所选的的调用operator &
。The operationx && y
is evaluated asT.false(x) ? x : T.&(x, y)
, whereT.false(x)
is an invocation of theoperator false
declared inT
, andT.&(x, y)
is an invocation of the selectedoperator &
. 换言之,x
首先计算并operator false
对结果调用,以确定x
是否肯定为 false。In other words,x
is first evaluated andoperator false
is invoked on the result to determine ifx
is definitely false. 然后,如果的x
值为 true,则运算的结果是先前为计算得出的值x
。Then, ifx
is definitely false, the result of the operation is the value previously computed forx
. 否则,y
将计算,并operator &
对之前为计算的值调用选择的,并对的值进行调用x
y
以生成操作的结果。Otherwise,y
is evaluated, and the selectedoperator &
is invoked on the value previously computed forx
and the value computed fory
to produce the result of the operation. - 运算的
x || y
计算结果为T.true(x) ? x : T.|(x, y)
,其中T.true(x)
是中声明的的调用operator true
T
,并且是对T.|(x,y)
所选的的调用operator|
。The operationx || y
is evaluated asT.true(x) ? x : T.|(x, y)
, whereT.true(x)
is an invocation of theoperator true
declared inT
, andT.|(x,y)
is an invocation of the selectedoperator|
. 换言之,x
首先计算并operator true
对结果调用,以确定是否x
确实为 true。In other words,x
is first evaluated andoperator true
is invoked on the result to determine ifx
is definitely true. 如果肯定为x
true,则运算的结果是先前为计算得出的值x
。Then, ifx
is definitely true, the result of the operation is the value previously computed forx
. 否则,y
将计算,并operator |
对之前为计算的值调用选择的,并对的值进行调用x
y
以生成操作的结果。Otherwise,y
is evaluated, and the selectedoperator |
is invoked on the value previously computed forx
and the value computed fory
to produce the result of the operation.
在上述任一操作中,由给定的表达式 x
只计算一次,并且由给定的表达式 y
不会进行任何计算或只计算一次。In either of these operations, the expression given by x
is only evaluated once, and the expression given by y
is either not evaluated or evaluated exactly once.
有关实现和的类型的示例 operator true
operator false
,请参阅 数据库布尔类型。For an example of a type that implements operator true
and operator false
, see Database boolean type.
Null 合并运算符The null coalescing operator
??
运算符称为 null 合并运算符。The ??
operator is called the null coalescing operator.
null_coalescing_expression
: conditional_or_expression
| conditional_or_expression '??' null_coalescing_expression
;
窗体的 null 合并表达式 a ?? b
需要 a
是可以为 null 的类型或引用类型。A null coalescing expression of the form a ?? b
requires a
to be of a nullable type or reference type. 如果 a
为非 null,则的结果 a ?? b
为 a
; 否则,结果为 b
。If a
is non-null, the result of a ?? b
is a
; otherwise, the result is b
. b
仅当为 null 时,操作才 a
进行评估。The operation evaluates b
only if a
is null.
Null 合并运算符是右结合运算符,这意味着运算从右到左分组。The null coalescing operator is right-associative, meaning that operations are grouped from right to left. 例如,窗体的表达式的 a ?? b ?? c
计算结果为 a ?? (b ?? c)
。For example, an expression of the form a ?? b ?? c
is evaluated as a ?? (b ?? c)
. 通常情况下,窗体的表达式将 E1 ?? E2 ?? ... ?? En
返回非 null 的第一个操作数,如果所有操作数为 null,则返回 null。In general terms, an expression of the form E1 ?? E2 ?? ... ?? En
returns the first of the operands that is non-null, or null if all operands are null.
表达式的类型 a ?? b
取决于在操作数上可用的隐式转换。The type of the expression a ?? b
depends on which implicit conversions are available on the operands. 按照优先顺序,的类型 a ?? b
为,, A0
或为,其中是具有类型) 的所提供 (的类型,是所) 提供的类型为的 (类型, A
B
A
a
a
B
b
b
A0
A
如果 A
是可以为 null 的类型,则为基础类型, A
否则为。In order of preference, the type of a ?? b
is A0
, A
, or B
, where A
is the type of a
(provided that a
has a type), B
is the type of b
(provided that b
has a type), and A0
is the underlying type of A
if A
is a nullable type, or A
otherwise. 具体而言, a ?? b
按如下方式进行处理:Specifically, a ?? b
is processed as follows:
- 如果
A
存在并且不是可以为 null 的类型或引用类型,则会发生编译时错误。IfA
exists and is not a nullable type or a reference type, a compile-time error occurs. - 如果
b
是动态表达式,则结果类型为dynamic
。Ifb
is a dynamic expression, the result type isdynamic
. 在运行时,a
首先计算。At run-time,a
is first evaluated. 如果a
不为 null,a
则将转换为动态,这将成为结果。Ifa
is not null,a
is converted to dynamic, and this becomes the result. 否则,b
将计算,这将成为结果。Otherwise,b
is evaluated, and this becomes the result. - 否则,如果
A
存在并且是可以为 null 的类型并且存在从到的隐式转换b
A0
,则结果类型为A0
。Otherwise, ifA
exists and is a nullable type and an implicit conversion exists fromb
toA0
, the result type isA0
. 在运行时,a
首先计算。At run-time,a
is first evaluated. 如果不为a
null,a
则将解包到类型A0
,这就成为了结果。Ifa
is not null,a
is unwrapped to typeA0
, and this becomes the result. 否则,b
将计算并转换为类型A0
,这将成为结果。Otherwise,b
is evaluated and converted to typeA0
, and this becomes the result. - 否则,如果
A
存在并且存在隐式转换b
A
,则结果类型为A
。Otherwise, ifA
exists and an implicit conversion exists fromb
toA
, the result type isA
. 在运行时,a
首先计算。At run-time,a
is first evaluated. 如果a
不为 null,则a
成为结果。Ifa
is not null,a
becomes the result. 否则,b
将计算并转换为类型A
,这将成为结果。Otherwise,b
is evaluated and converted to typeA
, and this becomes the result. - 否则,如果
b
具有类型B
且存在从到的隐式a
转换B
,则结果类型为B
。Otherwise, ifb
has a typeB
and an implicit conversion exists froma
toB
, the result type isB
. 在运行时,a
首先计算。At run-time,a
is first evaluated. 如果a
不为 null,a
则将解包为类型A0
(如果A
存在且可以为 null) 并转换为类型B
,则结果为。Ifa
is not null,a
is unwrapped to typeA0
(ifA
exists and is nullable) and converted to typeB
, and this becomes the result. 否则,b
将计算并返回结果。Otherwise,b
is evaluated and becomes the result. - 否则,
a
与b
不兼容,并发生编译时错误。Otherwise,a
andb
are incompatible, and a compile-time error occurs.
条件运算符Conditional operator
?:
运算符称为条件运算符。The ?:
operator is called the conditional operator. 有时也称为三元运算符。It is at times also called the ternary operator.
conditional_expression
: null_coalescing_expression
| null_coalescing_expression '?' expression ':' expression
;
表单的条件表达式 b ? x : y
首先计算条件 b
。A conditional expression of the form b ? x : y
first evaluates the condition b
. 然后,如果 b
为 true
, 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. 条件表达式绝不会计算 x
和 y
。A conditional expression never evaluates both x
and y
.
条件运算符是右结合运算符,这意味着运算从右到左分组。The conditional operator is right-associative, meaning that operations are grouped from right to left. 例如,窗体的表达式的 a ? b : c ? d : e
计算结果为 a ? b : (c ? d : e)
。For example, an expression of the form a ? b : c ? d : e
is evaluated as a ? b : (c ? d : e)
.
运算符的第一个操作数 ?:
必须是可以隐式转换为的表达式 bool
,或者是实现的类型的表达式 operator true
。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.
运算符的第二个和第三个操作数 x
y
?:
控制条件表达式的类型。The second and third operands, x
and y
, of the ?:
operator control the type of the conditional expression.
- 如果
x
具有类型X
并且y
具有类型,Y
则Ifx
has typeX
andy
has typeY
then- 如果隐式转换 (隐式 转换) 从
X
到Y
,但不Y
是从到X
,则Y
是条件表达式的类型。If an implicit conversion (Implicit conversions) exists fromX
toY
, but not fromY
toX
, thenY
is the type of the conditional expression. - 如果隐式转换 (隐式 转换) 从
Y
到X
,但不X
是从到Y
,则X
是条件表达式的类型。If an implicit conversion (Implicit conversions) exists fromY
toX
, but not fromX
toY
, thenX
is the type of the conditional expression. - 否则,不能确定表达式类型,并且会发生编译时错误。Otherwise, no expression type can be determined, and a compile-time error occurs.
- 如果隐式转换 (隐式 转换) 从
- 如果仅有一个
x
类型,且和都y
x
y
可隐式转换为该类型,则这是条件表达式的类型。If only one ofx
andy
has a type, and bothx
andy
, of are implicitly convertible to that type, then that is the type of the conditional expression. - 否则,不能确定表达式类型,并且会发生编译时错误。Otherwise, no expression type can be determined, and a compile-time error occurs.
此形式的条件表达式的运行时处理 b ? x : y
包括以下步骤:The run-time processing of a conditional expression of the form b ? x : y
consists of the following steps:
- 首先,
b
将计算,并bool
确定的值b
:First,b
is evaluated, and thebool
value ofb
is determined:- 如果从类型到的隐式转换
b
bool
存在,则执行此隐式转换以生成bool
值。If an implicit conversion from the type ofb
tobool
exists, then this implicit conversion is performed to produce abool
value. - 否则,将
operator true
调用类型的定义,b
以生成bool
值。Otherwise, theoperator true
defined by the type ofb
is invoked to produce abool
value.
- 如果从类型到的隐式转换
- 如果
bool
以上步骤生成的值为,则true
x
将计算并转换为条件表达式的类型,这将成为条件表达式的结果。If thebool
value produced by the step above istrue
, thenx
is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression. - 否则,
y
将计算并转换为条件表达式的类型,这将成为条件表达式的结果。Otherwise,y
is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression.
匿名函数表达式Anonymous function expressions
匿名函数 是表示 "内联" 方法定义的表达式。An anonymous function is an expression that represents an "in-line" method definition. 匿名函数本身不具有值或类型,但可以转换为兼容的委托或表达式树类型。An anonymous function does not have a value or type in and of itself, but is convertible to a compatible delegate or expression tree type. 匿名函数转换的计算取决于转换的目标类型:如果该类型为委托类型,则转换将计算为引用匿名函数定义的方法的委托值。The evaluation of an anonymous function conversion depends on the target type of the conversion: If it is a delegate type, the conversion evaluates to a delegate value referencing the method which the anonymous function defines. 如果该类型为表达式树类型,则转换为表达式树,该树将方法的结构表示为对象结构。If it is an expression tree type, the conversion evaluates to an expression tree which represents the structure of the method as an object structure.
由于历史原因,匿名函数有两种语法风格,即 lambda_expression s 和 anonymous_method_expression s。For historical reasons there are two syntactic flavors of anonymous functions, namely lambda_expression s and anonymous_method_expression s. 几乎所有情况下, lambda_expression 比 anonymous_method_expression 的更简洁、更具表现力,这仍然是用于向后兼容的语言。For almost all purposes, lambda_expression s are more concise and expressive than anonymous_method_expression s, which remain in the language for backwards compatibility.
lambda_expression
: anonymous_function_signature '=>' anonymous_function_body
;
anonymous_method_expression
: 'delegate' explicit_anonymous_function_signature? block
;
anonymous_function_signature
: explicit_anonymous_function_signature
| implicit_anonymous_function_signature
;
explicit_anonymous_function_signature
: '(' explicit_anonymous_function_parameter_list? ')'
;
explicit_anonymous_function_parameter_list
: explicit_anonymous_function_parameter (',' explicit_anonymous_function_parameter)*
;
explicit_anonymous_function_parameter
: anonymous_function_parameter_modifier? type identifier
;
anonymous_function_parameter_modifier
: 'ref'
| 'out'
;
implicit_anonymous_function_signature
: '(' implicit_anonymous_function_parameter_list? ')'
| implicit_anonymous_function_parameter
;
implicit_anonymous_function_parameter_list
: implicit_anonymous_function_parameter (',' implicit_anonymous_function_parameter)*
;
implicit_anonymous_function_parameter
: identifier
;
anonymous_function_body
: expression
| block
;
=>
运算符具有与赋值 () 相同的优先级 =
,并且是右结合运算符。The =>
operator has the same precedence as assignment (=
) and is right-associative.
带有修饰符的匿名函数 async
是一个异步函数,并遵循 异步函数中所述的规则。An anonymous function with the async
modifier is an async function and follows the rules described in Async functions.
可以显式或隐式类型地 lambda_expression 形式的匿名函数的参数。The parameters of an anonymous function in the form of a lambda_expression can be explicitly or implicitly typed. 在显式类型化参数列表中,每个参数的类型都是显式声明的。In an explicitly typed parameter list, the type of each parameter is explicitly stated. 在隐式类型的参数列表中,参数的类型是从发生匿名函数的上下文中推断出来的:具体而言,当匿名函数转换为兼容的委托类型或表达式目录树类型时,该类型提供 (匿名函数转换) 的参数类型。In an implicitly typed parameter list, the types of the parameters are inferred from the context in which the anonymous function occurs—specifically, when the anonymous function is converted to a compatible delegate type or expression tree type, that type provides the parameter types (Anonymous function conversions).
在具有单个隐式类型化参数的匿名函数中,可以从参数列表中省略括号。In an anonymous function with a single, implicitly typed parameter, the parentheses may be omitted from the parameter list. 换句话说,形式的匿名函数In other words, an anonymous function of the form
( param ) => expr
可以缩写为can be abbreviated to
param => expr
Anonymous_method_expression 格式的匿名函数的参数列表是可选的。The parameter list of an anonymous function in the form of an anonymous_method_expression is optional. 如果给定,则必须显式键入参数。If given, the parameters must be explicitly typed. 如果不是,则匿名函数可以转换为具有任何不包含参数的参数列表的委托 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_expression 和 anonymous_method_expression s 的行为是相同的,但以下情况除外:The behavior of lambda_expression s and anonymous_method_expression s is the same except for the following points:
- anonymous_method_expression 允许完全省略参数列表,则可以 convertibility 委托任何值参数列表的类型。anonymous_method_expression s permit the parameter list to be omitted entirely, yielding convertibility to delegate types of any list of value parameters.
- lambda_expression 允许省略和推断参数类型,而 anonymous_method_expression 则需要显式声明参数类型。lambda_expression s permit parameter types to be omitted and inferred whereas anonymous_method_expression s require parameter types to be explicitly stated.
- Lambda_expression 的主体可以是表达式或语句块,而 anonymous_method_expression 的主体必须是语句块。The body of a lambda_expression can be an expression or a statement block whereas the body of an anonymous_method_expression must be a statement block.
- 仅 lambda_expression s (表达式树类型) 转换为兼容的表达式树类型。Only lambda_expression s have conversions to compatible expression tree types (Expression tree types).
匿名函数签名Anonymous function signatures
匿名函数的可选 anonymous_function_signature 定义匿名函数的名称和可选的形参类型。The optional anonymous_function_signature of an anonymous function defines the names and optionally the types of the formal parameters for the anonymous function. 匿名函数的参数的作用域是 anonymous_function_body。The 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_expression 或 lambda_expression)相匹配。It is thus a compile-time error for the name of a parameter of the anonymous function to match the name of a local variable, local constant or parameter whose scope includes the anonymous_method_expression or lambda_expression.
如果匿名函数有 explicit_anonymous_function_signature,则兼容的委托类型集和表达式树类型将被限制为具有相同顺序的相同参数类型和修饰符的类型。If an anonymous function has an explicit_anonymous_function_signature, then the set of compatible delegate types and expression tree types is restricted to those that have the same parameter types and modifiers in the same order. 与方法组转换不同 (方法组转换) ,不支持匿名函数参数类型的逆变差。In contrast to method group conversions (Method group conversions), contra-variance of anonymous function parameter types is not supported. 如果匿名函数没有 anonymous_function_signature,则兼容的委托类型集和表达式树类型将被限制为没有参数的类型 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.
- 除了
ref
out
在签名中指定的或参数 (如果最近的封闭匿名函数有任何) ,则正文访问或参数的编译时错误ref
out
。Except forref
orout
parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access aref
orout
parameter. - 当的类型
this
为结构类型时,主体将访问的编译时错误this
。When the type ofthis
is a struct type, it is a compile-time error for the body to accessthis
. 无论访问是显式 (如) 还是隐式 (,这一点都是如此,this.x
x
其中x
,是结构) 的实例成员。This is true whether the access is explicit (as inthis.x
) or implicit (as inx
wherex
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_expression 或 anonymous_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 agoto
statement,break
statement, orcontinue
statement whose target is outside the body or within the body of a contained anonymous function. return
正文中的语句从最近的封闭匿名函数(而不是来自封闭函数成员)的调用返回控制权。Areturn
statement in the body returns control from an invocation of the nearest enclosing anonymous function, not from the enclosing function member. 语句中指定的表达式return
必须可隐式转换为委托类型或表达式目录树类型的返回类型,该类型或表达式树类型最接近的包含 lambda_expression 或 anonymous_method_expression 将 (匿名函数转换) 。An expression specified in areturn
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_expression 或 anonymous_method_expression。It is explicitly unspecified whether there is any way to execute the block of an anonymous function other than through evaluation and invocation of the lambda_expression or anonymous_method_expression. 特别是,编译器可以通过综合一个或多个命名方法或类型来选择实现匿名函数。In particular, the compiler may choose to implement an anonymous function by synthesizing one or more named methods or types. 任何此类合成元素的名称必须是保留供编译器使用的形式。The names of any such synthesized elements must be of a form reserved for compiler use.
重载决策和匿名函数Overload resolution and anonymous functions
自变量列表中的匿名函数参与类型推理和重载解析。Anonymous functions in an argument list participate in type inference and overload resolution. 请参阅 类型推理 和 重载决策 ,了解确切的规则。Please refer to Type inference and Overload resolution for the exact rules.
下面的示例演示匿名函数对重载决策的影响。The following example illustrates the effect of anonymous functions on overload resolution.
class ItemList<T>: List<T>
{
public int Sum(Func<T,int> selector) {
int sum = 0;
foreach (T item in this) sum += selector(item);
return sum;
}
public double Sum(Func<T,double> selector) {
double sum = 0;
foreach (T item in this) sum += selector(item);
return sum;
}
}
ItemList<T>
类有两种 Sum
方法。The ItemList<T>
class has two Sum
methods. 每个都采用一个 selector
自变量,该自变量从列表项中提取要求和的值。Each takes a selector
argument, which extracts the value to sum over from a list item. 提取的值可以是 int
或 double
,结果的总和也可以是 int
或 double
。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
,这两种 Sum
方法都适用,因为匿名函数 d => d. UnitCount
与 Func<Detail,int>
和兼容 Func<Detail,double>
。In the first invocation of orderDetails.Sum
, both Sum
methods are applicable because the anonymous function d => d. UnitCount
is compatible with both Func<Detail,int>
and Func<Detail,double>
. 但重载决策将选取第一 Sum
种方法,因为与 Func<Detail,int>
的转换更好于转换到的情况 Func<Detail,double>
。However, overload resolution picks the first Sum
method because the conversion to Func<Detail,int>
is better than the conversion to Func<Detail,double>
.
在的第二次调用中 orderDetails.Sum
,只有第二个 Sum
方法适用,因为匿名函数会 d => d.UnitPrice * d.UnitCount
生成类型为的值 double
。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_expression 或 anonymous_method_expression 的参数数组称为匿名函数的 外部变量 。Any local variable, value parameter, or parameter array whose scope includes the lambda_expression or anonymous_method_expression is called an outer variable of the anonymous function. 在类的实例函数成员中, this
值被视为值参数,并且是函数成员内包含的任何匿名函数的外部变量。In an instance function member of a class, the this
value is considered a value parameter and is an outer variable of any anonymous function contained within the function member.
捕获的外部变量Captured outer variables
当某个外部变量被匿名函数引用时,该外部变量被视为已被匿名函数 捕获 。When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. 通常情况下,局部变量的生存期仅限于执行它与) (局部变量 关联的块或语句。Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (Local variables). 但是,已捕获的外部变量的生存期至少会进行扩展,直到从匿名函数创建的委托或表达式树变为可进行垃圾回收的条件。However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.
示例中In the example
using System;
delegate int D();
class Test
{
static D F() {
int x = 0;
D result = () => ++x;
return result;
}
static void Main() {
D d = F();
Console.WriteLine(d());
Console.WriteLine(d());
Console.WriteLine(d());
}
}
局部变量 x
由匿名函数捕获,并至少扩展的生存期, x
直到从返回的委托 F
符合垃圾回收要求, (这在程序) 的最末尾之前不会发生。the local variable x
is captured by the anonymous function, and the lifetime of x
is extended at least until the delegate returned from F
becomes eligible for garbage collection (which doesn't happen until the very end of the program). 由于匿名函数的每个调用都在同一实例上运行 x
,因此该示例的输出为:Since each invocation of the anonymous function operates on the same instance of x
, the output of the example is:
1
2
3
当匿名函数捕获本地变量或值参数时,本地变量或参数将不再被视为固定变量 (固定和可移动的变量) ,而是被视为可移动的变量。When a local variable or a value parameter is captured by an anonymous function, the local variable or parameter is no longer considered to be a fixed variable (Fixed and moveable variables), but is instead considered to be a moveable variable. 因此 unsafe
,采用已捕获外部变量地址的任何代码都必须首先使用 fixed
语句来修复变量。Thus any unsafe
code that takes the address of a captured outer variable must first use the fixed
statement to fix the variable.
请注意,与 uncaptured 变量不同,捕获的局部变量可以同时公开给多个执行线程。Note that unlike an uncaptured variable, a captured local variable can be simultaneously exposed to multiple threads of execution.
局部变量的实例化Instantiation of local variables
当执行进入变量的作用域时,本地变量被视为被 实例化 。A local variable is considered to be instantiated when execution enters the scope of the variable. 例如,在调用以下方法时,本地变量 x
将实例化并初始化三次,一次针对循环的每次迭代。For example, when the following method is invoked, the local variable x
is instantiated and initialized three times—once for each iteration of the loop.
static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
但是,移动该循环外的声明会 x
导致单一实例化 x
: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;
}
这三个委托捕获的实例与的实例 x
不同 y
,输出如下:the three delegates capture the same instance of x
but separate instances of y
, and the output is:
1 1
2 1
3 1
单独的匿名函数可以捕获外部变量的同一个实例。Separate anonymous functions can capture the same instance of an outer variable. 在示例中:In the example:
using System;
delegate void Setter(int value);
delegate int Getter();
class Test
{
static void Main() {
int x = 0;
Setter s = (int value) => { x = value; };
Getter g = () => { return x; };
s(5);
Console.WriteLine(g());
s(10);
Console.WriteLine(g());
}
}
这两个匿名函数捕获本地变量的同一个实例 x
,因此它们可以通过该变量 "进行通信"。the two anonymous functions capture the same instance of the local variable x
, and they can thus "communicate" through that variable. 该示例的输出为:The output of the example is:
5
10
匿名函数表达式的计算Evaluation of anonymous function expressions
匿名函数 F
必须始终 D
E
直接或通过执行委托创建表达式来转换为委托类型或表达式树类型 new D(F)
。An anonymous function F
must always be converted to a delegate type D
or an expression tree type E
, either directly or through the execution of a delegate creation expression new D(F)
. 此转换确定匿名函数的结果,如 匿名函数转换中所述。This conversion determines the result of the anonymous function, as described in Anonymous function conversions.
查询表达式Query expressions
查询表达式 为类似于关系和分层查询语言(如 SQL 和 XQuery)的查询提供了语言集成语法。Query expressions provide a language integrated syntax for queries that is similar to relational and hierarchical query languages such as SQL and XQuery.
query_expression
: from_clause query_body
;
from_clause
: 'from' type? identifier 'in' expression
;
query_body
: query_body_clauses? select_or_group_clause query_continuation?
;
query_body_clauses
: query_body_clause
| query_body_clauses query_body_clause
;
query_body_clause
: from_clause
| let_clause
| where_clause
| join_clause
| join_into_clause
| orderby_clause
;
let_clause
: 'let' identifier '=' expression
;
where_clause
: 'where' boolean_expression
;
join_clause
: 'join' type? identifier 'in' expression 'on' expression 'equals' expression
;
join_into_clause
: 'join' type? identifier 'in' expression 'on' expression 'equals' expression 'into' identifier
;
orderby_clause
: 'orderby' orderings
;
orderings
: ordering (',' ordering)*
;
ordering
: expression ordering_direction?
;
ordering_direction
: 'ascending'
| 'descending'
;
select_or_group_clause
: select_clause
| group_clause
;
select_clause
: 'select' expression
;
group_clause
: 'group' expression 'by' expression
;
query_continuation
: 'into' identifier query_body
;
查询表达式以 from
子句开头,以 select
或子句结尾 group
。A query expression begins with a from
clause and ends with either a select
or group
clause. 初始 from
子句后面可以跟零个或多个 from
、 let
、 where
join
或 orderby
子句。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
子句都根据指定的条件对项进行重新排序。Final select
或 group
子句根据范围变量指定结果的形状。Each orderby
clause reorders items according to specified criteria.The final select
or group
clause specifies the shape of the result in terms of the range variables. 最后, into
子句可用于将一个查询的结果视为后续查询中的生成器来 "拼接" 查询。Finally, an into
clause can be used to "splice" queries by treating the results of one query as a generator in a subsequent query.
查询表达式中的多义性Ambiguities in query expressions
查询表达式包含许多 "上下文关键字",即在给定上下文中具有特殊意义的标识符。Query expressions contain a number of "contextual keywords", i.e., identifiers that have special meaning in a given context. 具体而言,包括、、、 from
where
join
on
、、、、、、、 equals
into
let
orderby
ascending
descending
select
group
和 by
。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). 具体而言,查询表达式被转换为名为、、、、、、、、、和的方法调用 Where
Select
SelectMany
Join
GroupJoin
OrderBy
OrderByDescending
ThenBy
ThenByDescending
GroupBy
Cast
。这些方法应具有特定的签名和结果类型,如 查询表达式模式中所述。Specifically, query expressions are translated into invocations of methods named Where
, Select
, SelectMany
, Join
, GroupJoin
, OrderBy
, OrderByDescending
, ThenBy
, ThenByDescending
, GroupBy
, and Cast
.These methods are expected to have particular signatures and result types, as described in The query expression pattern. 这些方法可以是要查询的对象的实例方法或对象外部的扩展方法,并实现查询的实际执行。These methods can be instance methods of the object being queried or extension methods that are external to the object, and they implement the actual execution of the query.
从查询表达式转换为方法调用是在执行任何类型绑定或重载决策之前发生的语法映射。The translation from query expressions to method invocations is a syntactic mapping that occurs before any type binding or overload resolution has been performed. 转换确保语法正确,但不保证生成语义正确的 c # 代码。The translation is guaranteed to be syntactically correct, but it is not guaranteed to produce semantically correct C# code. 转换查询表达式后,生成的方法调用将作为常规方法调用进行处理,这可能会导致错误(例如,如果方法不存在,如果参数具有错误的类型),或者如果方法是泛型,则类型推理将失败。Following translation of query expressions, the resulting method invocations are processed as regular method invocations, and this may in turn uncover errors, for example if the methods do not exist, if arguments have wrong types, or if the methods are generic and type inference fails.
通过重复应用以下翻译来处理查询表达式,直到不能进一步缩减。A query expression is processed by repeatedly applying the following translations until no further reductions are possible. 翻译按照应用程序顺序列出:每个部分都假设之前部分中的翻译已进行了详尽的操作,一旦完成,就不会再在处理同一查询表达式时再次使用部分。The translations are listed in order of application: each section assumes that the translations in the preceding sections have been performed exhaustively, and once exhausted, a section will not later be revisited in the processing of the same query expression.
不允许在查询表达式中分配范围变量。Assignment to range variables is not allowed in query expressions. 但允许 c # 实现并非始终强制实施此限制,因为有时可能不能使用此处提供的语法转换方案。However a C# implementation is permitted to not always enforce this restriction, since this may sometimes not be possible with the syntactic translation scheme presented here.
某些翻译注入范围变量,其中包含由表示的透明标识符 *
。Certain translations inject range variables with transparent identifiers denoted by *
. 透明标识符中进一步讨论了透明标识符的特殊属性。The special properties of transparent identifiers are discussed further in Transparent identifiers.
带有延续的 Select 和 groupby 子句Select and groupby clauses with continuations
具有延续的查询表达式A query expression with a continuation
from ... into x ...
转换为is translated into
from x in ( from ... ) ...
以下部分中的翻译假设查询没有 into
延续任务。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
from
显式指定范围变量类型的子句A from
clause that explicitly specifies a range variable type
from T x in e
转换为is translated into
from x in ( e ) . Cast < T > ( )
join
显式指定范围变量类型的子句A join
clause that explicitly specifies a range variable type
join T x in e on k1 equals k2
转换为is translated into
join x in ( e ) . Cast < T > ( ) on k1 equals k2
以下部分中的翻译假设查询没有显式范围变量类型。The translations in the following sections assume that queries have no explicit range variable types.
示例The example
from Customer c in customers
where c.City == "London"
select c
转换为is translated into
from c in customers.Cast<Customer>()
where c.City == "London"
select c
的最终转换是the final translation of which is
customers.
Cast<Customer>().
Where(c => c.City == "London")
显式范围变量类型用于查询实现非泛型接口的集合 IEnumerable
,但不能用于泛型 IEnumerable<T>
接口。Explicit range variable types are useful for querying collections that implement the non-generic IEnumerable
interface, but not the generic IEnumerable<T>
interface. 在上述示例中,如果的类型为,则会出现这种情况 customers
ArrayList
。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. 然后由 Select
和其他查询运算符的实现者来确保这些方法从不返回源对象本身。It is then up to the implementers of Select
and other query operators to ensure that these methods never return the source object itself.
From、let、where、join 和 orderby 子句From, let, where, join and orderby clauses
包含第二个 from
子句后跟子句的查询 select
表达式A query expression with a second from
clause followed by a select
clause
from x1 in e1
from x2 in e2
select v
转换为is translated into
( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )
一个查询表达式,该表达式的第二个 from
子句后跟除子句之外的其他内容 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 } )
...
带有子句的查询表达式 let
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 } )
...
带有子句的查询表达式 where
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
其后不 into
跟 select
子句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
into
后面跟有 select
子句A query expression with a join
clause with an into
followed by a select
clause
from x1 in e1
join x2 in e2 on k1 equals k2 into g
select v
转换为is translated into
( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
带有子句的查询表达式, join
后面跟有子句以外的 into
其他内容 select
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 })
...
带有子句的查询表达式 orderby
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 )
...
如果排序子句指定了 descending
方向指示器, OrderByDescending
则改为生成或的调用 ThenByDescending
。If an ordering clause specifies a descending
direction indicator, an invocation of OrderByDescending
or ThenByDescending
is produced instead.
以下翻译假设 let
where
join
orderby
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)
where x
和 y
是编译器生成的标识符,这些标识符不可见且不可访问。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
,、 y
和 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>
确保 ThenBy
和 ThenByDescending
方法仅可用于或的结果 OrderBy
OrderByDescending
。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
序列,其中每个内部序列都有一个附加 Key
属性。Also notice the recommended shape of the result of GroupBy
-- a sequence of sequences, where each inner sequence has an additional Key
property.
System.Linq
命名空间为实现接口的任何类型提供查询运算符模式的实现 System.Collections.Generic.IEnumerable<T>
。The System.Linq
namespace provides an implementation of the query operator pattern for any type that implements the System.Collections.Generic.IEnumerable<T>
interface.
赋值运算符Assignment operators
赋值运算符将新值分配给变量、属性、事件或索引器元素。The assignment operators assign a new value to a variable, a property, an event, or an indexer element.
assignment
: unary_expression assignment_operator expression
;
assignment_operator
: '='
| '+='
| '-='
| '*='
| '/='
| '%='
| '&='
| '|='
| '^='
| '<<='
| right_shift_assignment
;
赋值运算的左操作数必须是分类为变量、属性访问、索引器访问或事件访问的表达式。The left operand of an assignment must be an expression classified as a variable, a property access, an indexer access, or an event access.
=
运算符称为 简单赋值运算符。The =
operator is called the simple assignment operator. 它将右操作数的值分配给左操作数给定的变量、属性或索引器元素。It assigns the value of the right operand to the variable, property, or indexer element given by the left operand. 简单赋值运算符的左操作数不能是事件访问 (,只 需) 类似于字段的事件 中所述。The left operand of the simple assignment operator may not be an event access (except as described in Field-like events). 简单赋值运算符在 简单赋值中进行了介绍。The simple assignment operator is described in Simple assignment.
运算符以外的赋值运算符 =
称为 复合赋值运算符。The assignment operators other than the =
operator are called the compound assignment operators. 这些运算符对两个操作数执行指定的运算,然后将结果值分配给左操作数给定的变量、属性或索引器元素。These operators perform the indicated operation on the two operands, and then assign the resulting value to the variable, property, or indexer element given by the left operand. 复合赋值运算符在 复合赋值中介绍。The compound assignment operators are described in Compound assignment.
+=
-=
使用事件访问表达式作为左操作数的和运算符称为 事件赋值运算符。The +=
and -=
operators with an event access expression as the left operand are called the event assignment operators. 其他赋值运算符对于作为左操作数的事件访问无效。No other assignment operator is valid with an event access as the left operand. 事件赋值运算符在 事件分配中介绍。The event assignment operators are described in Event assignment.
赋值运算符是右结合运算符,这意味着运算从右到左分组。The assignment operators are right-associative, meaning that operations are grouped from right to left. 例如,窗体的表达式的 a = b = c
计算结果为 a = (b = c)
。For example, an expression of the form a = b = c
is evaluated as a = (b = c)
.
简单赋值Simple assignment
=
运算符称为简单赋值运算符。The =
operator is called the simple assignment operator.
如果简单赋值的左操作数为形式 E.P
或 E[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
,下面所述的解决方法将在运行时根据的运行时类型发生 E
。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.
简单分配窗体的运行时处理 x = y
包含以下步骤:The run-time processing of a simple assignment of the form x = y
consists of the following steps:
- 如果
x
归类为变量:Ifx
is classified as a variable:x
计算以生成变量。x
is evaluated to produce the variable.y``x
通过隐式转换 (隐式转换) 来计算并转换为的类型。y
is evaluated and, if required, converted to the type ofx
through an implicit conversion (Implicit conversions).- 如果给定的变量
x
是 reference_type 的数组元素,则执行运行时检查,以确保为计算的值与作为y
元素的数组实例兼容x
。If the variable given byx
is an array element of a reference_type, a run-time check is performed to ensure that the value computed fory
is compatible with the array instance of whichx
is an element. 如果为,则检查成功y
null
; 或者,如果隐式引用转换 (隐式引用 转换,) 存在从引用的实例的实际类型y
到包含的数组实例的实际元素类型x
。The check succeeds ify
isnull
, or if an implicit reference conversion (Implicit reference conversions) exists from the actual type of the instance referenced byy
to the actual element type of the array instance containingx
. 否则,将会引发System.ArrayTypeMismatchException
。Otherwise, aSystem.ArrayTypeMismatchException
is thrown. - 的计算和转换导致的值存储在的
y
计算中给定的位置x
。The value resulting from the evaluation and conversion ofy
is stored into the location given by the evaluation ofx
.
- 如果
x
归类为属性或索引器访问:Ifx
is classified as a property or indexer access:- 如果) 为,则实例表达式 (如果是,则为
x
static
x
; 如果是与相关联的索引器访问) ,则 (参数列表x
,并且在后续set
访问器调用中使用结果。The instance expression (ifx
is notstatic
) and the argument list (ifx
is an indexer access) associated withx
are evaluated, and the results are used in the subsequentset
accessor invocation. y``x
通过隐式转换 (隐式转换) 来计算并转换为的类型。y
is evaluated and, if required, converted to the type ofx
through an implicit conversion (Implicit conversions).set
调用的访问器时,将x
计算的值y
作为其value
参数。Theset
accessor ofx
is invoked with the value computed fory
as itsvalue
argument.
- 如果) 为,则实例表达式 (如果是,则为
数组协方差规则 (数组协方 差) 允许将数组类型的值 A[]
作为对数组类型的实例的引用 B[]
,前提是从到存在隐式引用转换 B
A
。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
最后一个分配导致 System.ArrayTypeMismatchException
引发,因为的实例 ArrayList
无法存储在的元素中 string[]
。the last assignment causes a System.ArrayTypeMismatchException
to be thrown because an instance of ArrayList
cannot be stored in an element of a string[]
.
如果在 struct_type 中声明的属性或索引器是赋值目标,则必须将与属性或索引器访问关联的实例表达式归类为变量。When a property or indexer declared in a struct_type is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. 如果实例表达式归类为值,则会发生绑定时错误。If the instance expression is classified as a value, a binding-time error occurs. 由于 成员访问,相同的规则也适用于字段。Because of Member access, the same rule also applies to fields.
给定以下声明:Given the declarations:
struct Point
{
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int X {
get { return x; }
set { x = value; }
}
public int Y {
get { return y; }
set { y = value; }
}
}
struct Rectangle
{
Point a, b;
public Rectangle(Point a, Point b) {
this.a = a;
this.b = b;
}
public Point A {
get { return a; }
set { a = value; }
}
public Point B {
get { return b; }
set { b = value; }
}
}
示例中in the example
Point p = new Point();
p.X = 100;
p.Y = 100;
Rectangle r = new Rectangle();
r.A = new Point(10, 10);
r.B = p;
p.X
允许对、、和的赋值, p.Y
r.A
r.B
因为 p
和 r
为变量。the assignments to p.X
, p.Y
, r.A
, and r.B
are permitted because p
and r
are variables. 但在示例中However, in the example
Rectangle r = new Rectangle();
r.A.X = 10;
r.A.Y = 10;
r.B.X = 100;
r.B.Y = 100;
分配均无效,因为 r.A
和 r.B
不是变量。the assignments are all invalid, since r.A
and r.B
are not variables.
复合赋值Compound assignment
如果复合赋值的左操作数为形式 E.P
或 E[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
,下面所述的解决方法将在运行时根据的运行时类型发生 E
。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,
- 如果所选运算符的返回类型可隐式转换为的类型
x
,则运算计算结果为x = x op y
,x
只计算一次。If the return type of the selected operator is implicitly convertible to the type ofx
, the operation is evaluated asx = x op y
, except thatx
is evaluated only once. - 否则,如果所选运算符为预定义运算符,则如果所选运算符的返回类型可显式转换为的类型
x
,并且如果可y
隐式转换为的类型x
或运算符为移位运算符,则运算将计算为x = (T)(x op y)
,其中T
是的类型x
,但x
只计算一次。Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type ofx
, and ify
is implicitly convertible to the type ofx
or the operator is a shift operator, then the operation is evaluated asx = (T)(x op y)
, whereT
is the type ofx
, except thatx
is evaluated only once. - 否则,复合分配无效,并发生绑定时错误。Otherwise, the compound assignment is invalid, and a binding-time error occurs.
术语 "仅计算一次" 表示在的计算中 x op y
,的所有构成表达式的结果 x
都将暂时保存,然后在执行对的赋值时重用 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
和是返回的方法 C
int
,这些方法只会按顺序调用一次 A
B
C
。For example, in the assignment A()[B()] += C()
, where A
is a method returning int[]
, and B
and C
are methods returning int
, the methods are invoked only once, in the order A
, B
, C
.
当复合赋值的左操作数是属性访问或索引器访问时,属性或索引器必须同时具有 get
访问器和访问器 set
。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. 存在该规则,以便当左操作数的类型为 sbyte
、 byte
、 short
、 ushort
或时,可以将预定义运算符用作复合运算符 char
。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= y
x op y
允许和都允许 x = y
。The intuitive effect of the rule for predefined operators is simply that x op= y
is permitted if both of x op y
and x = y
are permitted. 示例中In the example
byte b = 0;
char ch = '\0';
int i = 0;
b += 1; // Ok
b += 1000; // Error, b = 1000 not permitted
b += i; // Error, b = i not permitted
b += (byte)i; // Ok
ch += 1; // Error, ch = 1 not permitted
ch += (char)1; // Ok
每个错误的直观原因在于,相应的简单分配也会导致错误。the intuitive reason for each error is that a corresponding simple assignment would also have been an error.
这也意味着复合赋值操作支持提升的操作。This also means that compound assignment operations support lifted operations. 示例中In the example
int? i = 0;
i += 1; // Ok
使用提升运算符 +(int?,int?)
。the lifted operator +(int?,int?)
is used.
事件分配Event assignment
如果 or 运算符的左操作数 +=
-=
归类为事件访问,则表达式的计算方式如下:If the left operand of a +=
or -=
operator is classified as an event access, then the expression is evaluated as follows:
- 计算事件访问的实例表达式(如果有)。The instance expression, if any, of the event access is evaluated.
+=
计算或运算符的右操作数-=
,并在需要时通过隐式转换转换为左操作数的类型 (隐式转换) 。The right operand of the+=
or-=
operator is evaluated, and, if required, converted to the type of the left operand through an implicit conversion (Implicit conversions).- 调用事件的事件访问器,其中包含右操作数(在计算后,如有必要)转换后的参数列表。An event accessor of the event is invoked, with argument list consisting of the right operand, after evaluation and, if necessary, conversion. 如果运算符为
+=
,则add
调用访问器; 如果运算符为,则-=
remove
调用访问器。If the operator was+=
, theadd
accessor is invoked; if the operator was-=
, theremove
accessor is invoked.
事件赋值表达式不生成值。An event assignment expression does not yield a value. 因此,事件分配表达式仅在) statement_expression (表达式语句 的上下文中有效。Thus, an event assignment expression is valid only in the context of a statement_expression (Expression statements).
ExpressionExpression
表达式 可以是 non_assignment_expression 或 赋值。An expression is either a non_assignment_expression or an assignment.
expression
: non_assignment_expression
| assignment
;
non_assignment_expression
: conditional_expression
| lambda_expression
| query_expression
;
常量表达式Constant expressions
Constant_expression 是可以在编译时完全计算的表达式。A constant_expression is an expression that can be fully evaluated at compile-time.
constant_expression
: expression
;
常数表达式必须是 null
文本或具有以下类型之一的值:、、、、、、、、、、、、、、 sbyte
byte
short
ushort
int
uint
long
ulong
char
float
double
decimal
bool
object
string
或任何枚举类型。A constant expression must be the null
literal or a value with one of the following types: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
, bool
, object
, string
, or any enumeration type. 常量表达式中仅允许使用以下构造:Only the following constructs are permitted in constant expressions:
- 文本 (包括
null
文本) 。Literals (including thenull
literal). - 对
const
类和结构类型成员的引用。References toconst
members of class and struct types. - 对枚举类型的成员的引用。References to members of enumeration types.
- 对
const
参数或局部变量的引用References toconst
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.
checked
和unchecked
表达式checked
andunchecked
expressions- 默认值表达式Default value expressions
- Nameof 表达式Nameof expressions
- 预定义的
+
、、-
!
和~
一元运算符。The predefined+
,-
,!
, and~
unary operators. - 预定义的、、、、、、、、、、、、、、、、、、、、、、、
+
-
*
/
%
<<
>>
&
|
^
&&
||
==
!=
<
>
<=
和>=
二元运算符,前提是每个操作数都是上面列出的类型。The predefined+
,-
,*
,/
,%
,<<
,>>
,&
,|
,^
,&&
,||
,==
,!=
,<
,>
,<=
, and>=
binary operators, provided each operand is of a type listed above. ?:
条件运算符。The?:
conditional operator.
常数表达式中允许以下转换:The following conversions are permitted in constant expressions:
- 标识转换Identity conversions
- 数值转换Numeric conversions
- 枚举转换Enumeration conversions
- 常数表达式转换Constant expression conversions
- 隐式和显式引用转换,前提是转换的源是计算结果为 null 值的常量表达式。Implicit and explicit reference conversions, provided that the source of the conversions is a constant expression that evaluates to the null value.
不允许在常数表达式中使用其他转换,包括非 null 值的装箱、取消装箱和隐式引用转换。Other conversions including boxing, unboxing and implicit reference conversions of non-null values are not permitted in constant expressions. 例如:For example:
class C {
const object i = 5; // error: boxing conversion not permitted
const object str = "hello"; // error: implicit reference conversion
}
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.
除非将常量表达式显式放置在上下文中 unchecked
,否则在表达式的编译时计算过程中发生的溢出将始终导致编译时错误 (常数表达式) 。Unless a constant expression is explicitly placed in an unchecked
context, overflows that occur in integral-type arithmetic operations and conversions during the compile-time evaluation of the expression always cause compile-time errors (Constant expressions).
常数表达式出现在下面列出的上下文中。Constant expressions occur in the contexts listed below. 在这些上下文中,如果表达式在编译时无法完全计算,则会发生编译时错误。In these contexts, a compile-time error occurs if an expression cannot be fully evaluated at compile-time.
- 常数声明 (常量) 。Constant declarations (Constants).
- 枚举成员声明 (枚举 成员) 。Enumeration member declarations (Enum members).
- 形参的默认实参列出 (方法形参) Default arguments of formal parameter lists (Method parameters)
case``switch
switch 语句 () 的语句的标签。case
labels of aswitch
statement (The switch statement).goto case
goto 语句 (的语句) 。goto case
statements (The goto statement).- 数组创建表达式中的维度长度 (数组创建 表达式) 包含初始值设定项。Dimension lengths in an array creation expression (Array creation expressions) that includes an initializer.
- 特性 (特性) 。Attributes (Attributes).
隐式常量表达式转换 (隐式常量表达式转换) 允许将类型的常量表达式 int
转换为 sbyte
、、、、 byte
short
ushort
uint
或 ulong
,前提是常量表达式的值在目标类型的范围内。An implicit constant expression conversion (Implicit constant expression conversions) permits a constant expression of type int
to be converted to sbyte
, byte
, short
, ushort
, uint
, or ulong
, provided the value of the constant expression is within the range of the destination type.
布尔表达式Boolean expressions
Boolean_expression 是一个表达式,该表达式生成类型为的结果, bool
在某些上下文中直接或通过的应用程序, operator true
如下所示。A boolean_expression is an expression that yields a result of type bool
; either directly or through application of operator true
in certain contexts as specified in the following.
boolean_expression
: expression
;
If_statement 的 (if 语句的控制条件表达式) , while_statement (while 语句) , do_statement (语句) 或 for_statement ( ) 的 boolean_expression。The controlling conditional expression of an if_statement (The if statement), while_statement (The while statement), do_statement (The do statement), or for_statement (The for statement) is a boolean_expression. ?:
运算符 (条件运算符) 的控制条件表达式遵循与 boolean_expression 相同的规则,但出于运算符优先级的原因,将归类为 conditional_or_expression。The 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
在运行时隐式转换为,则应用隐式转换。IfE
is implicitly convertible tobool
then at runtime that implicit conversion is applied. - 否则,一元运算符重载决策 (一元运算符重载决策) 用于查找上运算符的唯一最佳实现
true
E
,并且该实现在运行时应用。Otherwise, unary operator overload resolution (Unary operator overload resolution) is used to find a unique best implementation of operatortrue
onE
, and that implementation is applied at runtime. - 如果找不到这样的运算符,则发生绑定时错误。If no such operator is found, a binding-time error occurs.
DBBool
数据库布尔类型中的结构类型提供了实现和的类型的示例 operator true
operator false
。The DBBool
struct type in Database boolean type provides an example of a type that implements operator true
and operator false
.