资源描述
表达式 105
7.1 表达式的分类 105
7.1.1 表达式的值 106
7.2 运算符 106
7.2.1 运算符的优先级和顺序关联性 106
7.2.2 运算符重载 107
7.2.3 一元运算符重载决策 108
7.2.4 二元运算符重载决策 109
7.2.5 候选用户定义运算符 109
7.2.6 数值提升 109
7.2.6.1 一元数值提升 110
7.2.6.2 二元数值提升 110
7.3 成员查找 111
7.3.1 基类型 111
7.4 函数成员 112
7.4.1 参数列表 114
7.4.2 重载决策 116
7.4.2.1 适用函数成员 116
7.4.2.2 更好的函数成员 117
7.4.2.3 更好的转换 117
7.4.3 函数成员调用 118
7.4.3.1 已装箱实例上的调用 119
7.5 基本表达式 119
7.5.1 文本 120
7.5.2 简单名称 120
7.5.2.1 块中的固定含义 121
7.5.3 带括号的表达式 122
7.5.4 成员访问 122
7.5.4.1 相同的简称和类型名称 123
7.5.5 调用表达式 124
7.5.5.1 方法调用 124
7.5.5.2 委托调用 125
7.5.6 元素访问 125
7.5.6.1 数组访问 126
7.5.6.2 索引器访问 126
7.5.7 this 访问 127
7.5.8 base 访问 127
7.5.9 后缀增量和后缀减量运算符 128
7.5.10 new 运算符 129
7.5.10.1 对象创建表达式 129
7.5.10.2 数组创建表达式 130
7.5.10.3 委托创建表达式 131
7.5.11 typeof 运算符 133
7.5.12 checked 和 unchecked 运算符 133
7.6 一元运算符 136
7.6.1 一元加运算符 136
7.6.2 一元减运算符 136
7.6.3 逻辑否定运算符 137
7.6.4 按位求补运算符 137
7.6.5 前缀增量和减量运算符 137
7.6.6 强制转换表达式 138
7.7 算术运算符 139
7.7.1 乘法运算符 139
7.7.2 除法运算符 140
7.7.3 余数运算符 141
7.7.4 加法运算符 142
7.7.5 减法运算符 144
7.8 移位运算符 146
7.9 关系和类型测试运算符 147
7.9.1 整数比较运算符 147
7.9.2 浮点比较运算符 148
7.9.3 小数比较运算符 149
7.9.4 布尔相等运算符 149
7.9.5 枚举比较运算符 149
7.9.6 引用类型相等运算符 149
7.9.7 字符串相等运算符 151
7.9.8 委托相等运算符 151
7.9.9 is 运算符 151
7.9.10 as 运算符 152
7.10 逻辑运算符 152
7.10.1 整数逻辑运算符 153
7.10.2 枚举逻辑运算符 153
7.10.3 布尔逻辑运算符 153
7.11 条件逻辑运算符 153
7.11.1 布尔条件逻辑运算符 154
7.11.2 用户定义的条件逻辑运算符 154
7.12 条件运算符 155
7.13 赋值运算符 156
7.13.1 简单赋值 156
7.13.2 复合赋值 158
7.13.3 事件赋值 159
7.14 表达式 159
7.15 常量表达式 159
7.16 布尔表达式 160
表达式
表达式是一个运算符和操作数的序列。本章定义语法、操作数和运算符的计算顺序以及表达式的含义。
表达式的分类
一个表达式可归类为下列类别之一:
值。每个值都有关联的类型。
变量。每个变量都有关联的类型,称为该变量的已声明类型。
命名空间。这类表达式只能出现在 member-access(第 0 节)的左边。在任何其他上下文中,归类为命名空间的表达式将导致编译时错误。
类型。这类表达式只能出现在 member-access(第 0 节)的左边,或作为 as 运算符(第 7.9.10 节)、is 运算符(第 7.9.9 节)或 typeof 运算符(第 7.5.11 节)的操作数。在任何其他上下文中,归类为类型的表达式将导致编译时错误。
方法组。它是一组重载方法,是成员查找(第 0 节)的结果。方法组可以有关联的实例表达式。当调用实例方法时,实例表达式的计算结果成为由 this(第 0 节)表示的实例。只能在 invocation-expression(第 0 节)或 delegate-creation-expression(第 7.5.10.3 节)中使用方法组。在任何其他上下文中,归类为方法组的表达式将导致编译时错误。
属性访问。每个属性访问都有关联的类型,即该属性的类型。此外,属性访问可以有关联的实例表达式。当调用实例属性访问的访问器(get 或 set 块)时,实例表达式的计算结果成为由 this(第 0 节)表示的实例。
事件访问。每个事件访问都有关联的类型,即该事件的类型。此外,事件访问还可以有关联的实例表达式。事件访问可作为 += 和 -= 运算符(第0)的左操作数出现。在任何其他上下文中,归类为事件访问的表达式将导致编译时错误。
索引器访问。每个索引器访问都有关联的类型,即该索引器的元素类型。此外,索引器访问还可以有关联的实例表达式和关联的参数列表。当调用索引器访问的访问器(get 或 set 块)时,实例表达式的计算结果成为由 this(第 0 节)表示的实例,而实参列表的计算结果成为调用的形参列表。
Nothing。这出现在当表达式是调用一个具有 void 返回类型的方法时。Nothing 类别的表达式仅在 statement-expression(第 Error! Reference source not found. 节)的上下文中有效。
表达式的最终结果绝不会是一个命名空间、类型、方法组或事件访问。恰如以上所述,这些类别的表达式是只能在特定上下文中使用的中间构造。
通过执行 get-accessor 或 set-accessor 的调用,属性访问或索引器访问总是被重新归类为值。特定访问器是由属性或索引器访问的上下文确定的:如果访问是赋值的目标,则调用 set-accessor 以赋新值(第 0 节)。否则调用 get-accessor 访问器以获取当前值(第 0 节)。
表达式的值
大多数含有表达式的构造最后都要求表达式表示一个值 (value)。在此情况下,如果实际的表达式表示命名空间、类型、方法组或 Nothing,则将发生编译时错误。但是,如果表达式表示属性访问、索引器访问或变量,则将它们隐式替换为相应的属性、索引器或变量的值:
变量的值只是当前存储在该变量所标识的存储位置的值。在可以获取变量的值之前,变量必须被视为已明确赋值(第 Error! Reference source not found. 节),否则将发生编译时错误。
一个属性访问表达式的值通过调用属性的 get-accessor 来获取。如果属性没有 get-accessor,则发生编译时错误。否则将执行一个函数成员调用(第 0 节),调用的结果成为属性访问表达式的值。
一个索引器访问表达式的值通过调用索引器的 get-accessor 来获取。如果索引器没有 get-accessor,则发生编译时错误。否则将使用与索引器访问表达式关联的参数列表来执行函数成员调用(第 0 节),调用的结果成为索引器访问表达式的值。
运算符
表达式由操作数 (operand) 和运算符 (operator) 构成。表达式的运算符指示对操作数进行什么样的运算。运算符的示例包括 +、-、*、/ 和 new。操作数的示例包括文本 (literal)、字段、局部变量和表达式。
有三类运算符:
一元运算符。一元运算符带一个操作数并使用前缀表示法(如 –x)或后缀表示法(如 x++)。
二元运算符。二元运算符带两个操作数并且全都使用中缀表示法(如 x + y)。
三元运算符。只有一个三元运算符 ?: 存在,它带三个操作数并使用中缀表示法 (c? x: y)。
表达式中运算符的计算顺序由运算符的优先级 (precedence) 和关联性 (associativity)(第 0 节)确定。
表达式中的操作数从左到右进行计算。例如,在 F(i) + G(i++) * H(i) 中,F 方法是使用 i 的旧值调用的,然后 G 方法也是使用 i 的旧值进行调用,最后 H 方法使用 i 的新值调用。这与运算符的优先级无关。
某些运算符可以重载 (overloaded)。运算符重载允许指定使用用户定义的运算符来执行某些运算,这些运算的操作数中至少有一个,甚至两个都属于用户定义的类或结构类型(第 0 节)。
运算符的优先级和顺序关联性
当表达式包含多个运算符时,运算符的优先级 (precedence) 控制各运算符的计算顺序。例如,表达式 x + y * z 按 x + (y * z) 计算,因为 * 运算符的优先级比 + 运算符高。运算符的优先级由运算符的关联语法产生式的定义确定。例如,一个 additive-expression 由以 + 或 - 运算符分隔的 multiplicative-expression 组成,因此给 + 和 - 运算符赋予的优先级比 *、/ 和 % 运算符低。
下表按照从最高到最低的优先级顺序概括了所有的运算符:
章节
类别
运算符
0
基本
x.y f(x) a[x] x++ x-- new
typeof checked unchecked
0
一元
+ - ! ~ ++x --x (T)x
0
乘除
* / %
0
加减
+ -
0
移位
<< >>
0
关系和类型检测
< > <= >= is as
0
相等
== !=
0
逻辑 AND
&
0
逻辑 XOR
^
0
逻辑 OR
|
0
条件 AND
&&
0
条件 OR
||
0
条件
?:
0
赋值
= *= /= %= += -= <<= >>= &= ^= |=
当操作数出现在具有相同优先级的两个运算符之间时,运算符的顺序关联性控制运算的执行顺序:
除了赋值运算符外,所有的二元运算符都向左顺序关联 (left-associative),意思是从左向右执行运算。例如,x + y + z 按 (x + y) + z 计算。
赋值运算符和条件运算符 (?:) 向右顺序关联 (right-associative),意思是从右向左执行运算。例如,x = y = z 按 x = (y = z) 计算。
优先级和顺序关联性都可以用括号控制。例如,x + y * z 先将 y 乘以 z,然后将结果与 x 相加,而 (x + y) * z 先将 x 与 y 相加,然后再将结果乘以 z。
运算符重载
所有一元和二元运算符都具有可自动用于任何表达式的预定义实现。除了预定义实现外,还可通过在类或结构(第 Error! Reference source not found. 节)中设置 operator 声明来引入用户定义的实现。用户定义的运算符实现的优先级总是高于预定义运算符实现的优先级:仅当没有适用的用户定义运算符实现存在时,才会考虑预定义的运算符实现,如第 0 节和第 7.2.4 节中所述。
可重载的一元运算符 (overloadable unary operator) 有:
+ - ! ~ ++ -- true false
虽然不在表达式中显式使用 true 和 false(并且因此而未包括在第 0 节的优先级表中),但仍将它们视为运算符,因为它们在多种表达式上下文中被调用:布尔表达式(第 0 节)和涉及条件运算符(第 7.12 节)以及条件逻辑运算符(第 7.11 节)的表达式。
可重载的二元运算符 (overloadable binary operator) 有:
+ - * / % & | ^ << >> == != > < >= <=
只有以上所列的运算符可以重载。具体而言,不可能重载成员访问、方法调用或 =、&&、||、?:、checked、unchecked、new、typeof、as 和 is 运算符。
当重载一个二元运算符时,也会隐式重载相应的赋值运算符(若有)。例如,运算符 * 的重载也是运算符 *= 的重载。第 0 节对此有进一步描述。请注意,赋值运算符本身 (=) 不能重载。赋值总是简单地将值按位复制到变量中。
强制转换运算(如 (T)x)通过提供用户定义的转换(第 Error! Reference source not found. 节)来重载。
元素访问(如 a[x])不被视为可重载的运算符。但是,可通过索引器(第 Error! Reference source not found. 节)支持用户定义的索引。
在表达式中,使用运算符表示法来引用运算符,而在声明中,使用函数表示法来引用运算符。下表显示了一元运算符和二元运算符的运算符表示法和函数表示法之间的关系。在第一项中,op 表示任何可重载的一元前缀运算符。在第二项中,op 表示 ++ 和 -- 一元后缀运算符。在第三项中,op 表示任何可重载的二元运算符。
运算符表示法
函数表示法
op x
operator op(x)
x op
operator op(x)
x op y
operator op(x, y)
用户定义的运算符声明总是要求至少一个参数为包含运算符声明的类或结构类型。因此,用户定义的运算符不可能具有与预定义运算符相同的签名。
用户定义的运算符声明不能修改运算符的语法、优先级或顺序关联性。例如,/ 运算符总是一个二元运算符,总是具有在第 0 节中指定的优先级,并且总是向左顺序关联。
虽然用户定义的运算符可以执行它想执行的任何计算,但是强烈建议不要采用产生的结果与直觉预期不同的实现。例如,operator == 的实现应比较两个操作数是否相等,然后返回一个适当的 bool 结果。
在从第 0 节到第 7.13 节的关于各运算符的说明中,运算符的预定义实现以及应用于各运算符的任何其他规则都有规定。在这些说明中使用了“一元运算符重载决策”(unary operator overload resolution)、“二元运算符重载决策”(binary operator overload resolution) 和“数值提升”(numeric promotion) 这样的术语,在后面的章节中可以找到它们的定义。
一元运算符重载决策
op x 或 x op 形式的运算(其中 op 是可重载的一元运算符,x 是 X 类型的表达式)按下面这样处理:
对于由 X 为运算 operator op(x) 提供的候选的用户定义运算符的集合,应根据第 0 节中的规则来确定。
如果候选的用户定义运算符集合不为空,则它就会成为运算的候选运算符集合。否则,预定义一元 operator op 实现成为关于该运算的候选运算符集合。关于给定运算符的预定义实现,在有关运算符的说明(第 0 节和第 7.6 节)中指定。
第 0 节中的重载决策规则应用于候选运算符集合,以选择一个关于参数列表 (x) 的最好的运算符,此运算符将成为重载决策过程的结果。如果重载决策未能选出单个最佳运算符,则发生编译时错误。
二元运算符重载决策
x op y 形式的运算(其中 op 是可重载的二元运算符,x 是 X 类型的表达式,y 是 Y 类型的表达式)按下面这样处理:
确定 X 和 Y 为运算 operator op(x, y) 提供的候选用户定义运算符集合。该集合包括由 X 提供的候选运算符和由 Y 提供的候选运算符的联合,每个候选运算符使用第 0 节中的规则确定。如果 X 和 Y 为同一类型,或者 X 和 Y 派生自一个公共基类型,则两者共有的候选运算符只在该并集中出现一次。
如果候选的用户定义运算符集合不为空,则它就会成为运算的候选运算符集合。否则,预定义二元 operator op 实现将成为该运算的候选运算符集合。关于给定运算符的预定义实现,在有关运算符的说明(第 0 节到第 7.13 节)中指定。
第 0 节中的重载决策规则应用于候选运算符集合,以选择一个关于参数列表 (x, y) 的最好的运算符,此运算符将成为重载决策过程的结果。如果重载决策未能选出单个最佳运算符,则发生编译时错误。
候选用户定义运算符
给定一个 T 类型和运算 operator op(A),其中 op 是可重载的运算符,A 是参数列表,对 T 为 operator op(A) 提供的候选用户定义运算符集合按下面这样确定:
对于 T 中的所有 operator op 声明,如果关于参数列表 A 至少有一个运算符是适用的(第 0 节),则候选运算符集合由 T 中所有适用的 operator op 声明组成。
否则,如果 T 为 object,则候选运算符集合为空。
否则,由 T 提供的候选运算符集合是 T 的直接基类提供的候选运算符集合。
数值提升
数值提升包括自动为预定义一元和二元数值运算符的操作数执行某些隐式转换。数值提升不是一个独特的机制,而是一种将重载决策应用于预定义运算符所产生的效果。数值提升尤其不影响用户定义运算符的计算,尽管可以实现用户定义运算符以表现类似的效果。
作为数值提升的示例,请看二元运算符 * 的预定义实现:
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);
当重载决策规则(第 0 节)应用于此运算符集合时,这些运算符中第一个能满足下述条件的运算符会被选中:存在一个从给定的操作数的类型到它声明的类型的隐式转换。例如,对于运算 b * s(其中 b 为 byte,s 为 short),重载决策选择 operator *(int, int) 作为最佳运算符。因此,效果是 b 和 s 转换为 int,并且结果的类型为 int。同样,对于 i * d 运算(其中 i 为 int,d 为 double),重载决策选择 operator *(double, double) 作为最佳运算符。
一元数值提升
一元数值提升是针对预定义的 +、– 和 ~ 一元运算符的操作数发生的。一元数值提升仅包括将 sbyte、byte、short、ushort 或 char 类型的操作数转换为 int 类型。此外,对于 – 一元运算符,一元数值提升将 uint 类型的操作数转换为 long 类型。
二元数值提升
二元数值提升是针对预定义的 +、–、*、/、%、&、|、^、==、!=、>、<、>= 和 <= 二元运算符的操作数发生的。二元数值提升隐式地将两个操作数都转换为一个公共类型,如果涉及的是非关系运算符,则此公共类型还成为运算的结果类型。二元数值提升应按下列规则进行(以它们在此出现的顺序):
如果有一个操作数的类型为 decimal,则另一个操作数转换为 decimal 类型;否则,如果另一个操作数的类型为 float 或 double,则发生编译时错误。
否则,如果有一个操作数的类型为 double,则另一个操作数转换为 double 类型。
否则,如果有一个操作数的类型为 float,则另一个操作数转换为 float 类型。
否则,如果有一个操作数的类型为 ulong,则另一个操作数转换为 ulong 类型;否则,如果另一个操作数的类型为 sbyte、short、int 或 long,则发生编译时错误。
否则,如果有一个操作数的类型为 long,则另一个操作数转换为 long 类型。
否则,如果有一个操作数的类型为 uint 而另一个操作数的类型为 sbyte、short 或 int,则两个操作数都转换为 long 类型。
否则,如果有一个操作数的类型为 uint,则另一个操作数转换为 uint 类型。
否则,两个操作数都转换为 int 类型。
请注意,第一个规则不允许将 decimal 类型与 double 和 float 类型混用。该规则遵循这样的事实:在 decimal 类型与 double 和 float 类型之间不存在隐式转换。
还需要注意的是,当一个操作数为有符号的整型时,另一个操作数的类型不可能为 ulong 类型。原因是不存在一个既可以表示 ulong 的全部范围,又能表示有符号整数的整型类型。
在以上两种情况下,都可以使用强制转换表达式显式地将一个操作数转换为与另一个操作数兼容的类型。
在下面的示例中
decimal AddPercent(decimal x, double percent) {
return x * (1.0 + percent / 100.0);
}
由于 decimal 类型不能与 double 类型相乘,因此发生编译时错误。通过将第二个操作数显式转换为 decimal 消除此错误,如下所示:
decimal AddPercent(decimal x, double percent) {
return x * (decimal)(1.0 + percent / 100.0);
}
成员查找
成员查找是确定类型上下文中的名称含义的过程。成员查找可能成为在表达式中计算 simple-name(第 0 节)或 member-access(第 7.5.4 节)过程的组成部分。
T 类型中的名称 N 的成员查找按下面这样处理:
首先,构造一个在 T 和 T 的基类型(第 Error! Reference source not found. 节)中声明的名为 N 的所有可访问(第 7.3.1 节)成员的集合。包含 override 修饰符的声明不包括在此集合中。如果名为 N 且可访问的成员不存在,则此查找不产生任何匹配,并且不对下面的步骤进行计算。
然后,从该集合中移除被其他成员隐藏的成员。对于该集合中的每个成员 S.M(其中 S 是声明了成员 M 的类型),应用下面的规则:
如果 M 是一个常量、字段、属性、事件、类型或枚举成员,则从集合中移除在 S 的基类型中声明的所有成员。
如果 M 是一个方法,则从集合中移除在 S 的基类型中声明的所有非方法成员,并从集合中移除与在 S 的基类型中声明的 M 具有相同签名的所有方法。
最后,移除了隐藏成员后,按下述规则确定查找的结果:
如果集合由一个非方法成员组成,则此成员为查找的结果。
否则,如果集合只包含方法,则这组方法为查找的结果。
否则,查找失败(无明确的结果),并发生编译时错误(只有对于具有多个直接基接口的接口中的成员查找才会出现这种情况)。
在不是接口的类型和严格单一继承的接口(继承链中的每个接口都只有零个或一个直接基接口)类型中,进行成员查找的规则可以简单地归结为:派生成员隐藏具有相同名称或签名的基成员。这种单一继承查找决不会失败(一定有明确的结果)。有关多重继承接口中的成员查找可能引起的多义性的介绍详见第 Error! Reference source not found. 节。
基类型
出于成员查找的目的,类型 T 被视为具有下列基类型:
如果 T 为 object,则 T 没有基类型。
如果 T 为 enum-type,则 T 的基类型为类类型 System.Enum、System.ValueType 和 object。
如果 T 为 struct-type,则 T 的基类型为类类型 System.ValueType 和 object。
如果 T 为 class-type,则 T 的基类型为 T 的基类,其中包括类类型 object。
如果 T 为 interface-type,则 T 的基类型为 T 的基接口和类类型 object。
如果 T 为 array-type,则 T 的基类型为类类型 System.Array 和 object。
如果 T 为 delegate-type,则 T 的基类型为类类型 System.Delegate 和 object。
函数成员
函数成员是包含可执行语句的成员。函数成员总是类型的成员,不能是命名空间的成员。C# 定义了以下类别的函数成员:
方法
属性
事件
索引器
用户定义运算符
实例构造函数
静态构造函数
析构函数
除了析构函数和静态构造函数(它们不能被显式调用),函数成员中包含的语句通过函数成员调用执行。编写函数成员调用的实际语法取决于具体的函数成员类别。
函数成员调用中所带的实参列表(第 0 节)提供了函数成员形参的实际值或变量引用。
调用方法、索引器、运算符和实例构造函数时,使用重载决策来确定要调用的候选函数成员集。有关此过程的介绍详见第 0 节。
在编译时(可能通过重载决策)确定了具体的函数成员后,有关运行时调用函数成员的实际过程的介绍详见第 0 节。
下表概述了在涉及六个可被显式调用的函数成员类别的构造中发生的处理过程。在下表中,e、x、y 和 value 代表变量或值类别的表达式,T 代表类型的表达式,F 是一个方法的简称,P 是一个属性的简称。
构造
示例
说明
方法调用
F(x, y)
应用重载决策以在包含类或结构中选择最佳的方法 F。以参数列表 (x, y) 调用该方法。如果该方法不为 static,则用 this 来表达对应的实例。
T.F(x, y)
应用重载决策以在类或结构 T 中选择最佳的方法 F。如果该方法不为 static,则发生编译时错误。以参数列表 (x, y) 调用该方法。
e.F(x, y)
应用重载决策以在 e 的类型给定的类、结构或接口中选择最佳的方法 F。如果该方法为 static,则发生编译时错误。用实例表达式 e 和参数列表 (x, y) 调用该方法。
属性访问
P
调用包含类或结构中属性 P 的 get 访问器。如果 P 是只写的,则发生编译时错误。如果 P 不是 static,则用 this 来表达对应的实例。
P = value
用参数列表 (value) 调用包含类或结构中的属性 P 的 set 访问器。如果 P 是只读的,则发生编译时错误。如果 P 不是 static,则用 this 来表达对应的实例。
T.P
调用类或结构 T 中属性 P 的 get 访问器。如果 P 不为 static,或者 P 是只写的,则发生编译时错误。
T.P = value
用参数列表 (value) 调用类或结构 T 中的属性 P 的 set 访问器。如果 P 不为 static,或者 P 是只读的,则发生编译时错误。
e.P
用实例表达式 e 调用由 e 的类型给定的类、结构或接口中属性 P 的 get 访问器。如果 P 为 static,或者 P 是只写的,则发生编译时错误。
e.P = value
用实例表达式 e 和参数列表 (value) 调用 e 的类型给定的类、结构或接口中属性 P 的 set 访问器。如果 P 为 static,或者如果 P 是只读的,则发生编译时错误。
事件访问
E += value
调用包含类或结构中的事件 E 的 add 访问器。如果 E 不是静态的,则用 this 来表达对应的实例。
E -= value
调用包含类或结构中的事件 E 的 remove 访问器。如果 E 不是静态的,则用 this 来表达对应的实例。
T.E += value
调用类或结构 T 中事件 E 的 add 访问器。如果 E 不是静态的,则发生编译时错误。
T.E -= value
调用类或结构 T 中事件 E 的 remove 访问器。如果 E 不是静态的,则发生编译时错误。
e.E += value
用实例表达式 e 调用由 e 的类型给定的类、结构或接口中事件 E 的 add 访问器。如果 E 是静态的,则发生编译时错误。
e.E -= value
用实例表达式 e 调用由 e 的类型给定的类、结构或接口中事件 E 的 remove 访问器。如果 E 是静态的,则发生编译时错误。
索引器访问
e[x, y]
应用重载决策以在 e 的类型给定的类、结构或接口中选择最佳的索引器。用实例表达式 e 和参数列表 (x, y) 调用该索引器的 get 访问器。如果索引器是只写的,则发生编译时错误。
e[x, y] = value
应用重载决策以在 e 的类型给定的类、结构或接口中选择最佳的索引器。用实例表达式 e 和参数列表 (x, y, value) 调用该索引器的 set 访问器。如果索引器是只读的,则发生编译时错误。
运算符调用
-x
应用重载决策以在 x 的类型给定的类或结构中选择最佳的一元运算符。用参数列表 (x) 调用选定的运算符。
x + y
应用重载决策以在 x 和 y 的类型给定的类或结构中选择最佳的二元运算符。用参数列表 (x, y) 调用选定的运算符。
实例构造函数调用
new T(x, y)
应用重载决策以在类或结构 T 中选择最佳的实例构造函数。用参数列表 (x, y) 调用该实例构造函数。
参数列表
每个函数成员调用均包括一个参数列表,该列表列出了函数成员参数的实际值或变量引用。如何指定函数成员调用的参数列表的语法取决于函数成员类别:
对于实例构造函数、方法和委托,参数被指定为 argument-list,如下所述。
对于属性,当调用 get 访问器时,参数列表是空的;而当调用 set 访问器时,参数列表由指定为赋值运算符的右操作数的表达式组成。
对于事件,参数列表由指定为 += 或 -= 运算符的右操作数的表达式组成。
对于索引器,参数列表由在索引器访问中的方括号之间指定的表达式组成。当调用 set 访问器时,参数列表还需附加上一个表达式,该表达式被指定为赋值运算符的右操作数。
对于用户定义的运算符,参数列表由一元运算符的单个操作数或二元运算符的两个操作数组成。
对于属性(第 Error! Reference source not found. 节)、事件(第 10.7 节)和用户定义运算符(第 10.9 节),它们的参数总是作为值参数(第 10.5.1.1 节)来传递。索引器(第 Error! Reference source not found. 节)的参数总是作为值参数(第 17.5.1.1 节)或参数数组(第 10.5.1.4 节)来传递。这些函数成员类别不支持引用参数和输出参数。
实例构造函数、方法或委托调用的参数按如下的 argument-list 形式指定:
argument-list:
argument
argument-list , argument
argument:
expression
ref variable-reference
out variable-reference
argument-list 由一个或多个 argument 组成,各参数之间用逗号隔开。每个参数都可以采用下列形式之一:
expression,指示将该参数作为值参数(第 Error! Reference source not found. 节)传递。
后跟 variable-reference(第 Error! Reference source not found. 节)的关键字 ref,指示将参数作为引用参数(第 10.5.1.2 节)传递。变量在可以作为引用参数传递之前,必须先明确赋值(第 Error! Reference source not found. 节)。后跟 variable-reference(第 Error! Reference source not found. 节)的关键字 out,指示将参数作为输出参数(第 10.5.1.3 节)传递。在将变量作为输出参数传递的函数成员调用之后,该变量被视为已明确赋值(第 Error! Reference source not found. 节)。
在一个函数成员调用(第 0 节)的运行时处理期间,将按顺序从左到右计算参数列表的表达式或变量引用,具体规则如下:
对于值参数,计算参数表达式并执行到相应的参数类型的隐式转换(第 Error! Reference source not found. 节)。结果值在函数成员调用中成为该值参数的初始值。
对于引用参数或输出参数,计算对应的变量引用,所得的存储位置在函数成员调用中成为该参数表示的存储位置。如果作为引用参数或输出参数给定的变量引用是一个 reference-type 的数组元素,则执行一个运行时检查以确保该数组的元素类型与参数的类型相同。如果检查失败,则引发 System.ArrayTypeMismatchException。
方法、索引器和实例构造函数可以将其最右边的参数声明为参数数组(第 Error! Reference source not found. 节)。是以正常形式还是以展开形式调用这类函数成员取决于哪种形式适用(第 0 节):
当以正常形式调用带有参数数组的函数成员时,为参数数组给定的参数必须是属于某个类型的单个表达式,而该类型可隐式转换(第 Error! Reference source not found. 节)为参数数组类型。在此情况下,参数数组的作用与值参数完全一样。
当以展开形式调用带有参数数组的函数成员时,调用必须为参数数组指定零个或更多参数,其中每个参数都是某个类型的表达式,而该类型可隐式转换(第 Error! Reference source not found. 节)为参数数组的元素类型。在此情况下,调用会创建一个该参数数组类型的实例,其所含的元素个数等于给定的参数个数,再用给定的参数值初始化此数组实例的每个元素,然后将新创建的数组实例用作实参。
参数列表的表达式总是按其书写的顺序来计算。因此,示例
class Test
{
static void F(int x, int y, int z) {
System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z);
}
static void Main() {
int i = 0;
F(i++, i++, i++);
}
}
产生输出
x = 0, y = 1, z = 2
如果存在从 B 到 A 的隐式引用转换,则数组协变规则(第 Error! Reference source not found. 节)允许数组类型 A[] 的值成为对数组类型 B[]
展开阅读全文