作者:思多雅[天行健
主要的表达式
primary-expr ession:
literal
simple-name
paren thesized-expression
member-access
invocation-expression
element-access
this-access
base-access
post -increment-expression
post -decrement-expression
new-expression
typeof -expression
sizeof-expression
checked-expression
unchecked-expression
1 文字
包含一个文字(§错误!未找到引用源。)的主要表达式被划分为数据。数据的类型根据下面的文字确定:
* 二进制文字是类型bool。这里有两个可能的二进制文字true和false。
* 整数文字是类型int、uint、long 或ulong,由文字的数值确定并且类型后缀可有可无。
* 实文字是类型float、double 或decimal,由文字的数值确定并且类型后缀可有可无。
l 字符文字是类型char 。
* 字符串文字是类型字符串。
* null 文字是类型null。
-------思多雅
2 简单名称
一个简单名称包含单个标识符。
simple-name:
identifi er
一个简单名称按下面进行求值和分类:
* 如果简单名称出现在块内,并且如果块中包含有给定名称的局部变量或参数,那么简单名称就是引用那个局部变量或参数,并且被分类为变量。
* 另外,对于每种类型T,由立即嵌套类、结构或枚举声明开始,后面跟着每个嵌套的外部类或结构声明(如果有),如果T 中的一个简单名称成员查询符合:
* 如果T 是立即嵌套类或结构类型并且查询确定一个或多个方法,结果是有一个方法组和一个相关的this 的实例表达式。
* 如果T 是立即嵌套类或结构类型,如果查询确定一个实例成员,并且如果引用发生在一个构造函数、一个实例方法或一个实例访问符的程序体中,结果与形式为this.E 的成员访问相同,这里E 是简单名称。
* 另外,结果与形式为T.E 的成员访问(§7.5.4)相同,这里E 是简单名称。在这种情况下,简单名称引用一个实例成员是个错误。
* 另外,从简单名称发生(如果有)的名称空间声明开始,跟着每个嵌套名称空间声明(如果有),并且用全局名称空间结束,下面的步骤会一直被求值,直到实体位置确定:
* 如果名称空间包含一个有给定名称的名称空间成员,那么简单名称指向那个成员,并且根据成员作为名称空间或类型被分类。
* 另外,如果名称空间声明包含把给定名称与输入名称空间或类型相关联的使用别名指示,那么简单名称引用那个名称空间或类型。
* 另外,如果被名称空间声明的使用名称空间指示引入的名称空间包含有给定名称的一个类型,那么简单名称引用那个类型。
* 另外,如果被名称空间声明的使用名称空间指示引入的名称空间包含有给定名称的超过一个类型,那么简单名称是不确定的并且会产生错误。
* 另外,由简单名称给出的名称是未定义的,并且会发生错误。
2.1 块中的不变量意义
每次出现把一个所给的标识符作为表达式中的简单名称的事件时,任何其它与立即嵌套块或转换块中表达式的简单名称相同的标识符都必须引用相同的实体这项规则保证一个表达式上下文中名称的意义在一个块中是相同的。
来看看这个例子:
class Test
{
double x;
void F(bool b) {
x = 1.0;
if (b) {
int x = 1;
}
}
}
是错误的,因为x 在外部块中(在if 语句中包含嵌套块的扩展)引用了不同的实体。相反,
来看看这个例子:
class Test
{
double x;
void F(bool b) {
if (b) {
x = 1.0;
}
else {
int x = 1;
}
}
}
是允许的,因为名称x 在外部块中永远不会使用。
这里要注意的是,不变量含义的角色指示应用于简单名称。一个标识符有一个作为简单名称的含义而另一个含义作为一个成员访问的右操作数。例如:
struct Point
{
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
上面的例子演示了在构造函数中作为参数名称的域的普通式样。在例子中,简单名称x 和y 引用参数,但是没有通过访问域来防止成员访问表达式this.x和this.y。
-------思多雅
3 加括号的表达式
一个加括号的表达式由一个附上一个括号的表达式组成。
paren thesized-expression:
( expression )
一个被加上括号的表达式通过对括号中的表达式进行求值来求值。如果括号中的表达式表示一个名称空间、类型或方法组,会产生错误。另外,加括号的表达式的结果是对包含的表达式的求值的结果。
-------思多雅
4 成员访问
一个成员访问由主要表达式或预定义类型,跟着一个“ .”符号,再跟着一个标识符来构成。
member-access:
primary-expr ession . identifi er
predefin ed-type . identifi er
predefin ed-type: one of
bool byte char decimal double float int long
object sbyte short string uint ulong ushort
一个形式为E.I 的成员访问是一个标识符,这里E 是一个主要的表达式或预定义的类型,它如下被求值和分类:
* 如果E 是一个名称空间而I 是那个名称空间的可访问成员的名称,那么结果是那个成员,并且根据这个成员被分类为一个名称空间或一个类型。
* 如果E 是一个预定义类型或一个分类为类型主要类型,而且E 中I 的成员查询(§7.3)产生了一个匹配,那么E.I 被如下求值和分类:
* 如果I 确定一个类型,那么结果就是那种类型。
* 如果I 确定一个或多个方法,那么结果是一个没有相关实例表达式的方法组。
* 如果I 确定了一个静态属性,那么结果是一个没有相关实例表达式的属性访问。
* 如果I 确定了一个静态域:
* 如果域是只读的,并且引用在声明了域的类或结构的静态构造函数外面发生,那么结果是一个数值,也就是E 中静态域I 的数值。
* 另外,结果是变量,也就是E 中的静态域I。
* 如果I 确定了一个静态事件:
* 如果引用发生于事件被声明的类或域中,那么E.I 就像I 是一个静态域或属性一样被处理。
* 另外,结果是一个没有相关实例表达式的事件访问。
* 如果I 确定一个常数,那么结果是数值,也就是那个常数的数值。
* 如果I 确定一个枚举成员,那么结果是数值,也就是那个枚举成员的数值。
* 另外,E.I 是一个无效的成员引用,这样就会发生错误。
* 如果E 是属性访问、索引访问、变量或数值,它的类型是T,并且I 的一个成员查询产生一个匹配,那么E.I 按下面被求值和分类:
* 首先,如果E 是一个属性或者索引访问,那么属性或索引访问的数值就被获得,并且E 被重新分类为一个数值。
* 如果I 确定一个或多个方法,那么结果是一个与E 的实例表达式相关的方法组。
* 如果I 确定一个实例属性,那么结果是一个与E 的实例表达式相关的属性访问。
* 如果T 是一个类类型,并且I 指定了那个类类型的一共实例域:
* 如果E 的数值是null,那么会抛出一个NullReferenceException异常。
* 另外,如果域是只读,并且引用在声明了域的类的实例构造函数外发生,那么结果是一个数值,也就是被E 引用的变量中的域I 的值。
* 另外,结果是一个变量,也就是被E引用的对象中的域I
* 如果T 是一共结构类型,并且I 指定了那个结构类型的实例域:
* 如果E 是一个数值,或者如果域是只读,并且引用发生在域被声明的结构的实例构造函数外面,那么结构是一共数值,也就是由E给出的结构实例中的域I 的数值。
* 另外,结果是一个变量,也就是由E给出的实例结构中的域I。
* 如果I 确定了一个实例事件:
* 如果引用发生在声明了事件的类或结构的外面,那么E.I 就像I 是一共实例域或属性一样被处理。
* 另外,结果是一个事件访问,并且与E 的实例表达式相关。
* 另外,E.I 是一个无效的成员引用,并且会发生错误。
4.1 同样的简单名称和类型名称
在形式E.I 的成员访问中,如果E 是一个简单标识符,并且如果E 的作为一个简单名称的意义是个常数、域、属性、局部变量或参数,与E 作为一个类型名称(§3.6)的意义相同,那么E 的两个可能的意义都是允许的。由于在所有情况I 都必须是E 的成员,因此E.I 的两个可能的含义不会是不明确的。换句话说,如果规则完全允许对E 的静态成员的访问,这里就会发生一个错误。例如:
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
}
}
在类A 中,那些引用Color 类型的Color 标识符的出现是被强调的,而那些引用Color 域的不被强调。
-------思多雅
5 调用表达式
一个调用表达式用来调用一个方法。
invocation-expression:
primary-expr ession ( argument-listopt )
调用表达式的主要表达式必须是一个代表类型的方法组或数值。如果主表达式是一个方法组,调用表达式就是一个方法调用。如果主表达式是一个代表类型的数值,那么调用表达式就是一个代表调用。如果主表达式既不是方法组也不是代表类型的数值,会产生错误。
可选的参数列表提供了数值或对方法的参数的引用。
对调用表达式求值的结果按下面进行分类:
* 如果调用表达式调用了一个返回void 的方法或代表,结果就是空。一个被分类为空的表达式不能是任何操作符的操作数,并且它只被允许存在于语句声明表达式的上下文中
* 另外,结果是被方法或代表返回的类型的数值。
5.1 方法调用
对于一个方法调用调用表达式的主表达式必须是一个方法组。这个方法组确定一个要调用的方法或一系列重载方法,从当中选择一个特殊的方法来调用。在后面的情况中,要调用的特殊方法的确定要基于由参数列表中的参数的类型提供的上下文。
编译时形式M(A)的方法调用过程由下面步骤组成:(这里M 是一个方法组,而A 是可选的参数列表)
* 方法调用的候选方法集合被构造。开始于与M 相关的方法集合,它可由前面的成员查询创建,这个集合被缩小到只包括那些与参数列表A 相关的可用方法。集合的缩小包括把下面的规则使用于集合中的每个方法T.N,这里T 是在声明了方法N 的类型:
* 如果N 与A(§7.4.2.1)不相关,那么N 被从集合中去掉。
* 如果N 与A(§7.4.2.1)相关,那么在T 的基本类型中声明的方法被从集合中去掉。
* 如果最后的候选方法集合是空的,那么就没有可用的方法存在,并且产生一个错误。如果候选方法并不是全都在相同的类型中声明,方法调用就是不明确的,会产生一个错误(这后面的一个情况只可能在有多个直接基接口的接口中一个方法的调用是发生)。
* 候选集合中最好的方法是使用中的重载分析规则来确定。如果不能确定单个最好的方法,那么方法调用是不明确的,并且会产生错误。
* 给定一个最好的方法,方法的调用在在方法组的上下文中是有效的:如果最好的方法是一个静态方法,方法组必须通过一种类型从简单名称或方法访问得到结果。如果最好的方法是实例方法,方法组必须从简单名称,通过一个变量或数值从成员访问或基访问来获得结果。如果这项需求都不能满足,就会产生一个编译时错误。
一旦一个方法已经被选择并且在运行时通过上面的步骤生效,实际运行时调用根据功能成员调用的规则来进行。
在上面描述的分析规则的直觉效果像下面一样:为了确定被方法调用所调用的方法的位置,从被方法调用指出的类型开始,并且沿着继承链向上找,直到至少找到一个在那个类型中声明的可使用、可访问非覆盖的方法,然后调用这样选择的方法。
5.2 代表调用
对于代表调用,调用表达式的基本表达式必须是一个代表类型的数值。而且,把代表类型看作有相同参数列表的功能成员,此代表类型必须可用于调用表达式的参数列表。
形式D(A)的代表调用的运行时过程由下面的步骤组成:(这里D 是一个代表类型的基本表达式,而A 是一个可选的参数列表。)
* D被求值,如果这个求值造成了一个例外,就不会进行下面的步骤。
* D 数值被检查为有效的。如果D 是null,就会抛出一个NullReferenceException并且不会进行下面的步骤。
* 另外,D 指向一个代表实例。一个功能成员调用(§7.4.3)在被代表引用的方法中实现。如果方法是一个实例方法,调用的实例就变为被代表引用的实例。
-------思多雅
6 元素访问
一个元素访问由几个部分组成:一个基本表达式,跟着一个“ [“符号,跟着一个表达式列表,跟着一个“]”符号。表达式列表由一个或多个表达式构成,用逗号分开。
element-access:
primary-expr ession [ express ion-list ]
expression-list:
expression
expression-list , expression
如果元素访问的基本表达式是一个数组类型的数值,那么元素访问世数组访问(§7.5.6.1)。另外,基本表达式必须是一个有一个或多个索引成员的类、结构或接口类型的变量或数值,并且元素访问就是一个索引访问。
6.1 数组访问
对于一个数组访问,元素访问的基本表达式必须是一个数组类型的数值。在表达式列表中的表达式的成员必须与数组类型的维数相同,而每个表达式必须是int 类型或是一个可以隐式地转换为int 的类型。
对一个数组访问求值的结果是这个数组的元素类型的变量,也就是在表达式列表中表达式的数值选择的数组成员。
形式P[A]的一个数组访问的运行时过程由下面的步骤组成:(这里P是数组类型的基本表达式而A 是一个表达式列表。)
* P被求值。如果这个求值造成了一个异常,下面的步骤就不会进行了。
* 表达式列表的索引表达式按从左到右的顺序求值。在对每个索引表达式求值后,会实现一个道类型int 的隐式转换。如果一个索引表达式的求值或后来的隐式转换(§6.1)造成了一个异常,那么不会对更多的索引表达式进行求值,而且不会执行下面的步骤。
* P 的数值被检查为有效的。如果P 的数值是null,就会抛出一个NullReferenceException异常,并且不会继续执行下面的步骤。
* 表达式列表中的每个表达式的数值都要被检查,来确定被P 引用的数组的每个维数都没有超过实际边界。如果一个或多个数值超出边界,就会抛出一个IndexOutOfRangeException异常,并且不会继续执行下面的步骤。
* 由索引表达式给出的数组元素位置是计算出来的,并且这个位置成为数组访问的结果。
6.2 索引访问
对于一个索引访问,元素访问的基本表达式必须是一个类、结构或接口类型的变量和数值,并且这个类型必须执行一个或多个可用于元素访问的表达式列表的索引。
形式P[A]的索引访问的编译时过程由下面的步骤组成:(这里P是一个类、结构或接口类型T 的基本表达式,而A 是一个表达式列表)
* 构造由T 提供的索引集合。这个集合由所有在T 中声明的索引,或T 中在当前的上下文中可访问而且不是隐藏的声明的类型组成。
* 这个集合被缩小到只包括那些可用的并且没有被其他索引隐藏的索引。下面的规则被应用于集合中的每个索引S.I,这里S 是一各声明了索引I 的类型:
* 如果I 不能用于A (§7.4.2.1),那么I 被从集合中去掉。
* 如果I 可用于A (§7.4.2.1),那么所有在基本类型S 中声明的索引都被从这个集合中去掉。
* 如果最后的候选索引的集合为空,那么就不会有可用索引存在,并且会产生一个错误。如果候选索引并不都在同一个类型中声明,索引访问就是不确定的,并且会产生错误。(后面的这种情况只会
在一个索引访问有多个直接基接口的接口的实例时发生。)
* 候选索引集合中最优索引使用§7.4.2中的重载分析规则来确定。如果不能确定一个最优的索引,索引访问就是不确定的,并且会发生错误。
* 索引访问执行的结果是一个被作为索引访问分类的表达式。索引访问表达式引用在前面步骤确定的索引,并且有一个相关的P 的实例表达式和一个相关的A 的参数列表。
根据所使用的上下文,一个索引访问引起对或者索引的get 访问符或者set 访问符的调用。如果索引访问一个赋值得目标,会调用set 访问符来赋新值(§错误!未找到引用源。)。在所有其他情况,调用get 访问符来获得当前数值。
6.3 字符串索引
字符串类实现了一个索引,它允许一个字符串的单个字符被访问。这个字符串类的索引按下面来声明:
public char this[int index] { get; }
换句话说,一个只读的索引诱一个类型为int 的参数,并且会返回一个char 类型的元素。为了索引参数传递的数值必须大于或等于零,并且要小于字符串的长度。