以下可跳过:
[立记保留:到现在为止,我满意的是认真的精神。--不放弃!(有双语的注释。)加强效率,抓重点。来看看非常好的一章内容,go--]
[MSDN,C#2]
表达式,操作符和操作数的序列(sequence)。这一篇将介绍语法、操作数和操作符的测算顺序(order),以及表达式的意义。
一、表达式归类
1.表达式归为下列类别之一:
(1)值,所有值都有已关联类型(associated)。
(2)变量,所有变量都有关联类型(变量的已声明类型(declared type))。
(3)名称,这种类别表达式只出现在成员访问(member-access)的左边。除此之外,在任何其它场合(context)使用都会引起编译时错误。
(4)类型,这种类别表达式只出现在成员访问的左边,或者作为as、is、typeof运算符的操作数(operand)。除此之外,在任何其它场合使用都会引起编译时错误。
(5)方法组(method group)
a.由成员查找(member lookup)产生(result from)的一组重载方法(a set of overloadded method);
b.方法组可以有关联的实例表达式。调用实例方法时,对实例表达式的测算结果成为(become)由this表示的(represented)实例。
c.方法组只允许在调用表达式(invocation-expression)和委托创建表达式(delegate-creation-expression)中使用,在其它场合使用归为方法组的表达式,会引起编译时错误。
(6)属性访问(property access)
a.所有属性访问都有关联类型(属性的类型(type of property))。
b.属性访问可以有关联的实例表达式。
c.调用实例属性访问的访问器(get或set块)时,实例表达式的结果成为this表示的实例。
(7)事件访问(event access)
所有事件访问都有关联类型(事件的类型)。此外,事件访问可以有关联的实例表达式,可以作为+=/-=操作符的左操作数出现。在任何其它场合使用事件访问表达式,会引起编译时错误。
(8)索引器访问(indexer access)
a.所有索引器访问都有关联类型,即索引器元素的类型。此外,索引器访问有(has)关联的实例表达式、关联的参数列表(argument list)。
b.调用索引器访问的访问器时,测算实例表达式的结果成为this表达的实例,测算自变量列表(argument list)的结果成为调用的参数列表(parameter list)。
(9)nothing
在表达式调用一个返回类型为void的方法时发生。仅用在语句表达式(statement-expression,简言之,表达式加';'构成语句表达式)的场合时是合法的(valid)。
--------------------------------------------------------------------------------------------------------------------------
2.表达式的最终结果永远不是名称空间、类型、方法组或事件访问,并且仅在某些场合(permitted in certain contexts),才允许上述类别(categories)的表达式作为中间构造(intermediate construct)来使用。
3.属性访问或索引器访问(access)总是被重新归置(reclassified)为值(方法是通过执行get或set访问器的调用),选择哪个访问器(particular accessor)是由属性或索引器访问的上下文决定,即如果访问是赋值的目的,则调用set访问器以赋新值;否则,调用get访问器以获得当前值。
4.表达式的值
大多数包含(involve)表达式的构造最终都要求(ultimately require)该表达式用来表示一个值(denote a value),而如果实际的表达式是表示(denote)名称空间、类型、方法组或"nothing",则发生编译时错误。
但,无论怎样(however),如果表达式表示的是属性访问、索引器访问或变量,那么属性、索引器、变量的值被隐式替代(substituted)。
a.变量的值,仅是存储在(通过)该变量标识的存储位置的当前值;在取得变量值之前,变量必须被明确赋值;否则发生编译时错误。
b.属性访问表达式的值,通过调用属性的get访问器取得。如果没有get访问器,就发生编译时错误;否则,执行函数成员调用,且调用结果成为属性访问表达式的值。
c.通过调用索引器的get访问器,取得索引器访问表达式(indexer access expression)的值。如果索引器中没有get访问器,则发生编译时错误;否则,执行带自变量列表--与索引器访问表达式相关联--的函数成员调用,该调用结果成为索引器访问表达式的值。
二、操作符
1.基础
(1)表达式由操作数(operand)和操作符来建立。表达式操作符指明应用于操作数的操作。
操作符是指+、-、*、/和"new"等;操作数是指文字(literal)、字段、本地变量和表达式等。
(2)操作符按操作数的数量分为三种,即一元操作符、二元操作符和三元操作符:
~一元操作符带一个操作数;一元符使用前缀(prefix)书写表示或后缀(postfix)书写表示(notation),如-x或x++;
~二元操作符带两个操作数;二元符使用中缀书写表示,如x+y;
~三元(ternary)操作符有三个操作数;三元符使用中缀表示(c#中只有一个三元符),?:,exists;,如(c?:x,y)。
(3)表达式中操作符的运算顺序是由操作符的优先级(precedence)和关联性(associativity)来决定。
(4)表达式中操作数从左到右测算。例如,在 F(i) + G(i++) * H(i)
中,使用 i
的旧值调用 F
方法,然后使用 i
的旧值调用 G
方法,最后使用 i
的新值调用 H
方法。这与操作符的优先级无关。
(5)某些(certain)操作符可以重载,操作符重载(operator overloading )允许用户自定义地为操作(operation)指明(specified)操作符实现(operator implementations),且该操作的一个操作数、或者两个操作数都是用户定义的类类型或结构类型(user-defined class or struct type)。
2.操作符优先级和关联性
(1)当表达式由多个操作符组成时,操作符的优先级规则控制着单个(individual)操作符的计算顺序。操作符的优先级是由它已关联语法的生成式(associated grammar production)的定义来建立。
(2)操作符由高到低的优先级顺序摘要:
基本 x.y f(x) a[x] x++ x-- netypeof checked unchecked
一元 + - ! ~ ++x --x (T)x 乘法 * / %
加法 + - 移位 << >>
关系和类型检测 < > <= >= is as
相等 == != 逻辑 AND &
逻辑 XOR ^ 逻辑 OR |
条件 AND && 条件 OR ||
条件 ?: 赋值 = *= /= %= += -= <<= >>= &= ^= |=
(3)当操作数发生在相同优先级的两个操作符之间时,操作符的关联(associativity)控制着执行操作的顺序。
1'除了赋值操作符以外,二元操作符都是左关联(left-associative);即操作按从左到右的顺序执行,如x+y+z计算为(x+y)+z;
2'赋值操作符和条件操作运算符(?:
)是右关联(right-associative);即操作按从右到左的顺序执行,如x=y=z计算为x=(y=z);
另,优先级和顺序关联可以用括号控制。
3.操作符重载(operator overloading)
(1)所有的一元和二元操作符,都有预定义实现,它们在任何表达式中自动可得。
(2)除预定义的实现以外,通过在类或结构中包含(including)操作符声明(operator declaration),可以在表达式中引入(introduced)用户自定义的实现(implementation);后者的优先级高于前者(用户定义的运算符实现的优先级总是高于预定义运算符实现:仅当没有适用的用户定义运算符实现时才会考虑预定义运算符实现。)
(3)下列一元操作符可重载:
+ - ! ~ ++ -- true false
因为会在几种表达式上下文环境中调用true/false,因此虽然不在表达式中显式使用它们,但仍将其视为操作符。如布尔表达式和涉及(involved)条件运算符/条件逻辑运算符的表达式。
(4)下列二元操作符可重载:
+ - * / % & | ^ << >> == != > < >= <=
(5) 除上述二元操作符外,不可能重载成员访问(member access)、方法调用(method invocation)或=, &&
, ||
, ?:
, checked
, unchecked
, new
, typeof
, as
, is
操作符。
(6)重载二元操作符时,也会自动(隐式)重载对应的赋值操作符(如果存在);如,重载*时,相应会重载*= ;
(7)赋值操作符本身不能被重载:赋值总是简单地把值按位复制(bit-wise copy)到变量中。
(8)强制转换操作(cast operation)如(T)x,通过提供用户自定义转换(user-defined conversion)来进行重载。
(9)元素访问(element access)象a[x],视为不可重载的操作符。但是,可通过索引器支持用户自定义的索引(indexing)。
(10)在表达式中,使用操作符表示法(notation)来引用操作符;在声明中,使用函数表示法来引用(referenced)操作符。下表显示了一元和二元操作符的操作符和函数表示之间的关系。
任何可重载的一元前缀运算符op x |
operator op(x) |
++ 和 op |
operator op(x) |
任何可重载的二元运算符 x op y |
operator op(x, y) |
(11)用户自定义的操作符声明,总是要求至少一个参数是包含(contains)运算符声明(operator declaration)的类或结构类型。正因如此,用户定义的操作符不会与预定义操作符的签名相同。
(12)用户自定义的操作符声明不能改变(modify)操作符的语法、优先级、或关联。例如,/
运算符总是一个二元运算符,总是具有指定的优先级,并且总是左关联。
(13)虽然用户定义的运算符可以执行它想执行的任何计算,但是强烈建议:不要采用产生的结果(produce results )与直觉预期(intuitively expected )不同的实现。例如,operator
==
的实现应比较两个操作数是否相等,然后返回一个适当的 bool
结果。
三、成员查找(member lookup)
(1)成员查找是对类型环境中的名称含义进行确定的处理。作为表达式中测算(evaluate)简称(simple name)或成员访问(member access)的一部分,可能而发生名称查找。
(2)如何在类型T中查找名称N?
1'所有在T和T的基类中声明、且名称为N的可访问成员的集合被建立起来;同时,将包含override修饰符的声明排除到集合外。如果名称是N的成员不存在而且不能访问(accessible),则查找不匹配,不往下执行下列步骤。
2'从该集合中移出被(其它成员)隐藏的成员,对集合中的每个成员S.M(成员M在类型S中声明),应用下列规则:
~如果M是一个常量、字段、属性、事件、类型或枚举成员,则从集合中移出S基类中声明的所有成员;
~如果M是一个方法,则从集合中移出S基类中声明的所有非方法成员,并,与M具有相同签名的所有方法;
3'移除了隐藏成员后,最后来确定查找的结果:
~如果集合包含了单个非方法成员,则此成员为查找的结果。否则,
~如果集合只包含方法,则该组方法即为查找的结果。否则,
~查找失败(无明确的结果),并引起编译时错误(这种情况,只出现在接口中的成员查找时接口具有多个直接基接口(direct base interface)的情况)。
4'对类型(而不是接口)的成员查找、以及接口中的成员查找是严格的单一继承(single-inheritance,继承链中的每个接口都只有零个或一个直接基接口)这种情况,成员查找的规则可以简单归结为:派生成员隐藏具有相同名称或签名(name or signature)的基成员。这时的单一继承查找一定会有明确的结果(never ambiguous)。
5'因为成员查找,类型T被视为具有下列基类型(base types):
~object没有基类型;
~值类型T的基类型,是类类型object;
~类类型T的基类型,是T的基类(base classes),包括类类型object;
~接口类型T的基类型,是T的基接口(base interface)和类类型object;
~数组类型T的基类型,是类类型System.Array和object;
~委托类型T的基类型,是类类型System.Delegate和object。
四、函数成员
包含可执行语句(executable statements)的成员,它总是类类型的成员。分类如下:
方法;属性;事件;索引器;用户定义的操作符;实例构建器;实例构建器;静态构建器;析构器。详见下一篇。
五、基本表达式(primary expression)--表达式最简单形式
基本表达式分为数组创建的表达式和非数组创建的表达式,该分类用来避免出现潜在的代码混乱(confusing code).格式为
primary-no-array-creation-expression(非数组创建基本表达式)
array-creation-expression(数组创建表达式)
非数组创建的基本表达式,包括如下部分:
- literal(文本),包含文本的基本表达式归为值;
- simple-name(简称),由单个的标识符构成(就是单一标识符),格式为 simple-name: identifier
简称的匹配:
(1)如果简称在(一个)块内出现,而且该块(或封闭块)的本地变量声明空间(space)包含给定名称的局部变量或参数,则该简称引用那个本地变量或参数,归为变量(类别);
(2)否则,对每个类型 T,
从随即的(immediately)封闭的类(enclosing class)、结构或枚举声明开始,持续(遍历)(continuing with)包含它的外部(outer)类或结构声明(如存在);如果T
中简称(simple-name)的成员查找(member lookup)产生一个匹配,那么:
a.如果T
为随即封闭它的类、或结构类型,且该查找识别出(identifies)一个或多个方法,则结果是一个关联实例表达式this
的方法组;
b.如果T
为随即封闭它的类、或结构类型,且该查找识别出一个实例成员,并且该引用发生在实例构造器、实例方法或实例访问器的块中,则结果与this.E
形式的成员访问相同(其中E
为简称);
c.否则,结果与T.E
形式的成员访问相同(其中 E
为简称)。这种情况下,简称引用(refer to)一个实例成员会产生一个编译时错误。
d.否则,从出现该简称的名字空间开始,持续遍历每个包含它的名字空间(若存在),并且以全局命名空间(global namespace)结束,按下列步骤进行测算,直到找到实体(entity is located):
1'如果名字空间包含一个给定名称的名字空间成员,则简称引用该成员。并依据该成员把简称归到名字空间或类型(类别)。
2'否则,如果该名字空间有个对应的名字空间声明,且简称出现的位置(enclosing the location)包含在内,那么:
~如果名字空间声明包含一个“using别名指令(using alias directive)”,它把给定的名称(name)与导入的(imported)名字空间或类型相关联。则,该简称引用(refers to)这个名字空间或类型。否则,
~如果名字空间声明使用“using 名字空间指令(using namespace directive)”来导入名字空间,且该名字空间只(exactly)包含一个给定名称(given name)的类型,则简称引用这个类型。否则,
~如果在上述导入的名字空间中,包含有多个(more than one type)具有给定名称的类型,则简称不明确(ambiguous )并发生编译时错误。否则,
~由简称给定的名称就被认为是未经定义(undefined),导致编译时错误。
(3)简称在块中含义不变性(invariant meaning in blocks)
在表达式中每当发生给定的标识(identifier)作为简称应用,而且直接包含它的(immediately enclosing)块或switch块内,相同的简称标识如果在表达式中再次出现(other occurrence)时,为确保块内表达式上下文中的名称含义相同,该简称被要求引用共同的实体(same entity):
//因为x引用了外部块中不同的实体,所以产生编译时错误 class Test { double x; void F(bool b) { x = 1.0; if (b) { int x; x = 1; } } } //以下是正确的用法 class Test { double x; void F(bool b) { if (b) { x = 1.0; } else { int x ; x = 1; } } }
- parenthesized-expression(带括号的表达式)
(1)括号表达式是由括号括起(enclosed)的表达式,它的语法格式为: ( expression )
(2)它的计算是计算括号中的表达式,如果括号内的表达式表示名字空间、类型或方法组,则发生编译时错误。否则,带括号的表达式的结果为所含表达式的计算结果。
- member-access(访问成员)
1.成员访问是由基本表达式(primary)或预定义类型组成,后接“.”标记,再接一个标识符;它的语法为
predefined-type . identifier [predefined-type: bool byte char decimal double float int long object sbyte short string uint ulong ushort 之一
2.成员访问的形式是E.I,其测算(evaluate)及分类如下:
(1)E是名字空间,I是它的可访问成员名,则结果是该成员,且以该成员归为名字空间或类型;
(2)E归为类型(type)的预定义类型或基本表达式,且E中对I的成员查找有匹配,则E.I估算及分类按下列规则:
#I标识一个类型,则结果是该类型;
#I标识一个或多个方法,则结果是方法组,该组没有关联的实例表达式;
#I标识一个静态属性,则结果是属性访问,该访问没有关联的实例表达式;
#I标识一个静态字段:
~该字段只读(readonly),并且引用发生于字段声明所在类或结构的-静态构建器外部,则结果是值(E中静态字段I的值);否则,结果是变量(E中的静态字段I)。
#I标识一个静态事件:
~(成员访问)引用是发生在声明事件的类或结构中,且事件声明时没有事件访问符声明(event-accessor-declarations),那么对E.I的处理,和I作为静态字段一样;否则,结果是没有关联实例表达式的事件访问。
#I标识一个常量,那么结果是值(常量的值);
#I标识一个枚举成员,那么结果是值(枚举成员的值);否则,
#E.I是非法的成员引用,发生编译时错误。
(3)E是属性访问、索引器访问、变量或值,它们的类型是T,且T对I的查找有匹配,则测算E.I且分类如下:
#E是一个属性或索引器访问,则获得它的值,且E重新归(reclassified)为值;
#I标识一个或多个方法,则结果是一个关联实例表达式E的方法组;
#I标识一个实例属性,则结果是一个关联实例表达式E的实例访问;
#T是类类型,I是标识该类类型的实例字段:
~E的值为null,会引发System.NullReferenceException;否则,
~字段只读,且引用发生在声明该字段的类的实例构建器之外,那么结果是值(E的对象引用中字段I的值);否则,结果是变量(E的对象引用中的字段I);
#T是结构类型,且I标识它的一个实例字段:
~E是一个值,或者:如果字段只读,且引用发生在声明该字段的结构的实例构建器之外,那么结果是一个值(E给定结构实例中的字段I的值);否则,结果是一个变量(E给定的结构实例中的字段I);
#I标识一个实例事件,
~引用发生在声明事件的类或结构内,事件声明没有事件访问器声明(event accessor declaration),则对E.I地处理完全类似于I是一个实例字段;否则,结果是一个关联事件表达式E的事件访问;
(4)否则,E.I是无效的成员引用,发生编译时错误。
(5)相同的(identical)简称和类型名称
~在E.I形式的成员访问中,如果E是单个的标识符,且作为简称意义上的E与作为类型名称意义上的E,是具有相同的类型(the same type)的-常量、字段、属性、本地变量或参数,则E的这两种可能的意义都是允许的。
~以上两种情况下,I都必须一定(must necessarily)是类型E的成员,所以E.I两种可能的含义从来都是确定的(never ambiguous)。
~换言之,该规则仅仅(simply)是允许原本会发生编译时错误的--对E的静态成员和嵌套类型的访问(the rule simply permits access to the static members and nested types of E
where a compile-time error would otherwise have occurred.)。
//在类 A 中,引用 Color 类型的 Color 标识符的那些匹配项带下划线,而引用 //Color 字段的那些匹配项不带下划线。 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 } }
- invocation-expression(行使表达式)
(1)用于援引(某个)方法,语法为
~基本表达式必须是方法组,或委托类型的值。如果是方法组,则它是方法调用;如果是委托类型的值,则是委托调用;如果都不是,会发生编译时错误;
~(可选的)自变量列表即(某个)方法的参数(parameters of the method),它是值或变量引用;
(2)行使表达式的测算结果如下:
如果行使表达式援引方法或委托而返回(return)"void"时,则其结果类别就是"nothing"。即,该表达式只能用在语句表达式环境,而不能当作(任何)操作符的操作数;否则,结果是方法或委托返回类型的值。
(3)具体的方法调用,略。可参见7.5.5.1.
(4)具体的委托调用,略。可参见7.5.5.2.
- element-access(访问元素)
(1)元素访问是由基本非数组创建表达式构成,其后跟'['标记(token)和']'标记括起的表达式列表。表达式列表由一个或多个由逗号分隔的表达式构成。
a.元素访问的语法为:
element-access:
primary-no-array-creation-expression [ expression-list ]
expression-list:
expression
expression-list , expression
b.如果表达式是数组类型的值,访问元素就是访问数组(array access);否则,
c.它必须是一个变量,或类、结构或接口类型的值。后者属于索引器访问,它由一个或多个索引器成员构成。
(2)数组访问,
1'对于数组访问,元素访问的非数组创建基本表达式的值必须是数组类型。表达式列表中的表达式数目必须与数组类型的秩(rank)相同,并且每个表达式都必须是 int
、uint
、long
、ulong
等类型,或者是可隐式转换为这些类型中的一个或多个类型的类型。
2'数组访问的测算结果是数组元素的变量(表达式列表中表达式的值选定的数组元素)。数组访问采用P[A]
形式(其中 P
是数组类型的非数组创建基本表达式,A
是表达式列表),运行时处理如下:
~计算P
。如果此计算导致异常,则不执行进一步的操作。
~(表达式列表的)索引表达式按从左到右的顺序计算。计算每个索引表达式后,执行隐式转换到下列类型之一:int
、uint
、long
、ulong
,并选择此列表中存在隐式转换的第一个类型。例如,如果索引表达式是 short
类型,则执行到 int
的隐式转换,这是因为可以执行从short
到int
和从short
到long
的隐式转换。如果计算索引表达式或后面的隐式转换时导致异常,则不再进一步计算索引表达式,并且不再执行进一步的操作。
~检查P
的值是否有效。如果P
的值为null
,则引发System.NullReferenceException
,并且不再执行进一步的操作。
~针对由P
引用的数组实例的每一维的实际界限(actual bounds),检查“表达式列表”(expression-list)中每个表达式的值。如果一个或多个值超出了范围,则引发System.IndexOutOfRangeException
,并且不再执行进一步的操作。
~计算由索引表达式给定的数组元素的位置,此位置将成为数组访问的结果。
(3)索引器访问(涉及函数,挂起。可参见7.5.6.2)
- this-access(this 访问)
(1)this访问由关键字this构成;它的语法为 this-access: this
(2)this访问只能在实例构建器、实例方法、实例访问器(instance accessor)的块中使用,具有下列含义:
~用在(class)类的实例构建器内的基表达式(primary expression)时,this归类为值。该值的类型是发生使用(usage occurs)所在的类,值是对被创建对象(object being constructed)的引用;
~用
在类(class)的实例方法或实例访问器内的基表达式中时,this归类为值。该值的类型是发生使用所在的类,值是对行使(invoked)方法或访问器的对象(object)的引用;
~用在结构的实例构造函数内的基表达式中时,this归类为变量。该变量的类型是此表达式所在的结构,并且该变量表示的是被构建的结构。结构的实例构造函数的this
变量的行为与结构类型的out
参数完全一样,具体说来,这表示该变量在实例构造函数的每个执行路径中必须已明确赋值。
~用在结构的实例方法或实例访问器内的基本表达式中时,this归类为变量。该变量的类型是此表达式所在的结构,并且该变量表示的是行使方法或访问器的结构。结构实例方法的this
变量的行为与结构类型的ref
参数完全一样。
(6)在非上述“环境”的基本表达式中使用this会导致
编译时错误。即,不能在静态方法、静态属性访问器中或字段声明的“变量初始值设定项”中引用this
!
- base-access(base 访问)
(1)base访问是由关键字base后跟'.' 标记以及标识符,或者括在方括号内的表达式列表构成;语法格式为
base [ expression-list ]
(2)base访问用来访问基类成员,因为该基类成员被当前的类或结构内的同名成员所隐藏;
(3)base访问同this一样,只能用在实例构建器、实例方法、实例访问器的块内;
(4)在类或结构中使用base.I时,I必须表示那个类或结构的基类的成员;同样地(likewise)在某类中使用base[E]时,在其基类中也必须存在适用的(applicable)索引器;
(5)在编译时,base.I
和 base[E]
形式的base访问表达式完全(exactly)和((B)this).I
和((B)this)[E]
)(B
是发生构建(construct occur)的类或结构的基类)一样(进行测算。所以,除了this 被视为基类的实例以外,base.I
和base[E]分别
对应于this.I
和this[E]
。
(6)基类引用虚函数成员(virtual function member-某个方法、属性或索引器),在运行时(run-time),用于确定援引哪个成员的规则-和一般规则不同(is changed),确定调用哪一个函数成员的方法是:
1'被“援引”的函数成员是取决于找到相对B(而非常规的非base访问-是相对于this的运行时类型)的派生程度最大的实现(most derived implementation);
2'因此,在虚函数成员的override中,(一个)base访问可用于'援引'该函数成员被继承了的实现(inherited implementation);
3'如果base访问所引用的函数成员是抽象的,那么发生编译时错误。
- post-increment/decrement-expression(后缀增量/减量表达式)
(1)语法格式为
(2)后缀增减量操作的操作数(即,语法中的基本表达式部分)必须是归为变量或属性访问或索引器访问的表达式,操作的结果是值,该值和操作数的类型相同;
(3)如果后缀增减量操作的操作数是属性访问或索引器访问,那么属性必须同时具有get和set访问器;非如此,编译时发生错误;
(4)一元操作符(unary operator)重载决策(resolution)用来选择(select)特定的操作符实现,存在预定义的++和--操作符的类型:sbyte, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
, 枚举类型。预定义的++
操作符返回操作数加上1的结果值,预定义的--
操作符返回操作数减去1的结果值。
(5)对于形式为x++或x--的后缀增减量操作,运行时按以下步骤处理:
1'如果x是变量:
测算x,产生变量->保存x的值->调用选定的(selected)操作符,把x的保存值作为它的参数(argument)->操作符返回的值存储在x
的测算(evaluation of x)所给定的位置中->x存储的值成为操作的结果。
2'如果x是属性或索引器访问
计算与x关联的实例表达式(如果x是非静态)和参数列表(如果x是索引器访问),结果用于后面的get和set访问器调用->调用x的get访问器,并且保存返回值->用x的保存值作为参数,调用给定的操作符->调用x
的set
访问器,把操作符返回的值作为它的值参数->x的保存值成为操作的结果。
(6)++和--操作符还支持前缀符,x++/x--的结果是'操作前'x的值;++x/--x的结果是'操作后'x的值;无论这两种情况的哪一种,虽然它们的结果不同,但在执行(运算)操作后,x本身都具有相同的值;
(7)可以使用后缀或前缀表示(notation)中的一种来行使操作符++或操作符的实现;但两种表示不可能有各自的操作符实现(separate operator implementations )。
- object/delegate-creation-expression(对象/委托创建表达式)
new操作符用于创建类型的新实例;
new操作符含有创建类型实例的意义,但并不暗示一定要动态分配内存。具体说(in particular),除了它们驻留的变量以外(variables reside),值类型的实例并不要求额外的内存。所以当new用于创建值类型的实例时,并不发生动态分配。
new有三种表达式形式:
1.对象创建表达式,用于创建类类型和值类型的新实例;
a.它的语法形式为:new type ( argument-listopt )
其中的type必须是类类型或值类型,但不能是抽象的类类型;只有type是类类型或结构类型时,可以使用自变量列表;
b.new T(A)形式的对象创建表达式:T是类类型或值类型,A是可选的自变量列表,包含下列编译时处理步骤:
1'T是值类型,且A不存在
这种情况,对象创建表达式是个默认构建器调用,调用的结果是类型T的值(称作T的缺省值);
2'否则,T是类类型或结构类型:
~T是抽象的类类型,发生编译时错误;
~通过使用重载决策规则以确定援引(to invoke)哪个实例构造器:候选的(canditate)实例构造器集合包括T中声明的所有可访问的实例构建器,它们是可应用于A的(applicable with respect to A)。如果候选的实例构建器集合为空(empty),或无法标识出单个的最佳实例构建器,则发生编译时错误;
3'对象创建表达式的结果是类型T的值,称作按(determined)上述步骤确定的、通过调用实例构建器产生的值;
4'否则,该对象创建表达式非法,产生编译时错误;
c.new T(A)形式的对象创建表达式(T是类类型或结构类型,A是可选的自变量列表)包含下列运行时处理步骤:
1'T是类类型:
~为类(class)T的新实例分配存储空间(is allocated),如果内存不足,抛出System.OutOfMemoryException,不再往下继续执行;
~为新实例的所有字段初始化默认值;
~按行使函数成员的规则,援引实例构建器;把新分配了空间的实例引用(a reference)自动传给实例构造器,可以用this从构建器内部访问(accessed from within)该实例;
2'T是结构类型:
~类型(type)T的实例是分配一个临时的本地变量来创建。在创建实例时,要求结构类型的实例构建器必须对该实例的所有字段明确赋值,故不必对该临时变量进行初始化(no initialization);
~实例构建器的调用遵循函数成员调用的规则,把新分配了空间的实例引用自动传给实例构造器,可以用this从构建器内部访问该实例。
2.数组创建表达式,用于创建数组类型的新实例;
a.array-creation-expression:
new array-type array-initializer
2'~在第二种形式的数组创建表达式中,指定的数组类型的秩必须匹配数组初始化器的秩。单个维的长度(individual dimension length)是从-数组初始化器(array initializer)每个对应的嵌套级别(corresponding nesting levels)的-元素的数量(number of elements)-推断得出,因此,表达式
new int[,] {{0, 1}, {2, 3}, {4, 5}}完全对应于new int[3, 2] {{0, 1}, {2, 3}, {4, 5}}
b.数组创建表达式的测算结果属于值类别(对新分配的数组实例的引用)。数组创建表达式的运行时处理步骤:
~表达式列表的维长度表达式按从左到右的顺序进行测算。测算每个表达式之后,执行一个到下列类型之一的隐式转换int, uint
, long
, ulong,选择列表中第一个存在着隐式转换的那个类型。如果表达式的测算或随后的隐式转换导致一个异常,则不再进行表达式测算和执行其它步骤。
~维长度的计算值按以下校验。如果一个或多个值小于0,则抛出System.OverflowException异常,不需要继续执行。
~数组实例按给定的维长度(dimension length)来分配空间(allocated),如果分配给新实例的空间不足,System.OutOfMemoryException异常被抛出,不需要再执行。
~新数组实例的所有元素被初始化为默认值。
~如果数组创建表达式包含一个数组初始化器(array initializer),那么数组初始化器中的每个表达式被测算并赋值给对应的数组元素,测算和赋值的顺序按(数组初始化器中)书写(written)表达式的顺序进行。
换言之,元素按递增的索引顺序(increasing index order)进行初始化(initialized),最右侧的维先递增。如果给定表达式的测算或(随后)相应数组元素的赋值出现异常,则不对其它元素进行初始化(剩余的元素将因此具有它们的默认值)。
c.数组创建表达式允许对元素是数组(类型)的数组进行初始化,但必须通过手工(编程)来初始化数组元素。如,
int[][] a = new int[100][];
该语句创建100个元素类型为int[]的一维数组,每个元素的初始值为null,不可能通过同一个数组创建表达式同时实例化它的子数组,因此语句int[][] a = new int[100][5];会导致一个编译时错误。
子数组的初始化必须手工执行,如下例
int[][] a = new int[100][];
for (int i = 0; i < 100; i++) a[i] = new int[5];
当数组的数组具有“矩形”形状时,即当子数组全都具有相同的长度时,使用多维数组更有效。在上面的示例中,实例化一个数组的数组时,实际上创建了 101 个对象(1 个外部数组和 100 个子数组)。相反,
int[,] = new int[100, 5];
只创建单个对象(即一个二维数组)并在单个语句中完成分配。
3.委托创建表达式,用于创建委托类型的新实例。
delegate-creation-expression: new delegate-type ( expression )
a.委托创建表达式的自变量必须是个方法组,或委托类型的值:如果自变量是一个方法组则用来标识方法,且对象为实例方法创建(一个)委托;如果自变量是一个委托类型的值,它标识(创建副本(copy)的)委托实例。
b.对new D(E)形式的委托创建表达式(D是委托类型,E是表达式),它在编译时的处理过程如下:
1'E是一个方法组:
~由E所标识的方法集(set)必须包含一个完全兼容D的方法(exactly one method),并且该方法成为新创建的委托所引用的方法;如果匹配的方法不存在,或存在多于一个的匹配方法,发生编译时错误。如果选定的(selected)方法是个实例方法,和E关联的实例表达式确定委托的目标对象;
~与行使方法类似,选定的方法必须与方法组的上下文兼容(compatible):如果方法是静态方法,方法组必须已经从某类型(a type)的简称或成员访问产生(result from);如果方法是实例方法,方法组必须已经从简称或对变量或值的成员访问产生。如果选定的方法和方法组的“环境”不匹配,则发生编译时错误;
~结果是类型D的值,称为引用选定方法和目标对象的新创建委托。
2'否则,E是一个委托类型的值:
~D和E必须兼容;否则,产生编译时错误;
~结果是类型D的值,称为与E(一样)引用相同的援引列表的新创建委托;
c.否则,委托创建表达式是非法的,发生一个编译时错误。
d.对new D(E)形式的委托创建表达式,其中D是委托类型而E是表达式,它在运行时的处理过程如下:
1'E是一个方法组:
#如果选定的方法在编译时是静态方法,委托的目标对象(target object)是null。否则,选定的方法是一个实例方法,且委托的目标对象是由与E关联的实例表达式决定:
~测算实例表达式。如果测算产生异常,不再继续执行;
~实例表达式属于引用类型,由实例表达式计算得到的值成为目标对象。如果目标对象是null,抛出System.NullReferenceException异常。不再继续执行;
~实例表达式属于值类型, 执行上箱操作以把值转换为对象,该对象成为目标对象;
#为委托类型D新实例分配空间,如果内存不足,则抛出异常System.OutOfMemoryException。不再执行其它步骤;
#新的委托实例是对在编译时确定的(determined)方法的引用(reference),和对上面计算的目标对象的引用来初始化。
2'E是一个委托类型的值:
~测算E。如该测算产生异常,则不需要进一步执行;
~如果E的值为null,抛出System.NullReferenceException,并且不需要继续执行;
~为委托类型D的新实例分配存储空间,如果没有足够的内存,System.OutOfMemoryException 异常抛出。不需要进一步执行;
~用与 E
给定的委托实例相同的援引列表初始化新委托实例。
e.在委托实例化时决定委托的援引列表,并且在委托的整个生命周期保持不变。换句话说,委托一旦创建,它的可调用目标实体(callable entities)就不能改变。当组合两个委托或从一个委托中移除另一个委托时,将产生新委托;不需要委托更改内容。
f.不可能创建引用属性、索引器、用户定义的操作符、实例构建器、析构器或静态构建器的委托。
g.如上所述,当从一个方法组创建一个委托时,形参(formal parameter)列表和委托的返回类型,决定了选择哪一个重载方法。
//A.f字段用委托来初始化,且引用第二个Square方法 //这是因为它完全匹配DoubleFunc的形参列表和返回类型 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; } }
- typeof-expression(typeof 表达式)
a.typeof操作符用于获取某个对象的System.Type对象;
b.格式为 typeof-expression:
typeof ( void )
~typeof表达式的第一种形式是由typeof关键字后跟一个括号括起的type构成。该形式的表达式的结果是对所指类型(indicitated type)的System.Type对象。对于任何给定的类型,只有对应System.Type的唯一对象。这意味着对类型T,typeof(T)==typeof(T)始终为真;
~typeof表达式的第二种形式是由typeof关键字后跟一个括号括起的void构成。这种形式的表达式的结果是表示不存在类型的(absence of a type)System.Type对象。由typeof(void)返回的类型对象不同于(distinct from)为任何类型返回的类型对象(type object)。这种特殊的类型对象(意指typeof返回的类型对象)--对于允许在语言中对方法进行反射的类库(class libraries)非常有用,它用于这种情况:这些方法希望(或需要)一种使用System.Type实例来表示任何方法的返回类型的方式。
//定义一个类型数组Type[] t using System; class Test { static void Main() { Type[] t = { typeof(int), typeof(System.Int32), typeof(string), typeof(double[]), typeof(void) }; for (int i = 0; i < t.Length; i++) { Console.WriteLine(t[i].FullName); } } } //其输出为 //System.Int32 //System.Int32 //System.String //System.Double[] //System.Void
- checked/unchecked-expression(checked/unchecked 表达式)
(1)checked和unchecked操作符控制上下文中,整型算术操作和转换的溢出检查。
(2)格式为
(3)checked操作符测算在checked上下文中所包含的表达式,unchecked操作符测算在unchecked上下文中所包含的表达式。除了在给定的溢出检查上下文中对所包含的表达式进行测算外,checked表达式或unchecked表达式与带括号的表达式完全对应(corresponds exactly)。
(4)溢出检查上下文也可以由checked和unchecked语句来控制。
(5)下列操作受溢出检查上下文(由checked和unchecked操作符和语句建立)的影响:
~当操作数是一个整型时,预定义的“++/--”一元操作符;
~当操作数是一个整型时,预定义的“-”一元操作符;
~当两个操作数都是整数类型时,预定义的“+-*/”二元操作符;
~从一个整数类型到另一个整数类型的显式数值转换,或者从float/double到整数类型;
(6)当上述的操作之一产生的结果太大以至不能在终极类型(destination type)中表示时,执行操作地上下文控制着结果行为:
~在checked的上下文中,如果操作是常量表达式,编译时错误发生;否则,在运行时执行操作,抛出一个System.OverflowException 异常;
~在unchecked的上下文中,通过放弃任何不适合目标类型的高序位以截断结果;
(7)对任何没有用checked和unchecked操作符/语句括起来的(enclosed)非常量表达式(在运行时测算的表达式),除非外部因素(如编译器开关和执行环境配置)调用(call for)checked测算,否则默认的溢出检查环境为unchecked;
对于常量表达式(在编译时表达式可以被完全测算),缺省的溢出检查环境总是checked,除非把常量表达式显式地置于unchecked环境中,表达式在编译时测算的溢出总是导致编译时错误。
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
发表评论
-
ZT,一篇好文
2010-05-03 10:33 999读读语言大牛们(其中有位是Anders)这篇文章,会很受益。( ... -
关于“匿名方法”与“lambda操作符”的实现
2010-03-24 22:29 871在<关于“回调”的实现>一文中,我们探讨了使用委托 ... -
effective hierarchy(二)之 函数合辑(2)
2009-09-16 21:29 727介绍: 本节我们来看看另外一种封装,构建件。构建件作为 ... -
effective hierarchy(二)之 函数合辑(1)
2009-08-19 14:53 781“暴露”阶段:值-> ... -
effective hierarchy(一)之 基本概念(8)
2009-07-02 23:39 685MSDN,C#2 一、语句 1.结束点和可达性(en ... -
effective hierarchy(一)之 基本概念结束篇
2009-06-30 15:41 662一、函数/功能成员(fun ... -
effective hierarchy(一)之 基本概念(6)
2009-06-21 00:34 949MSDN,C#2.0 转换(conversions) ... -
effective hierarchy(一)之 基本概念(5)
2009-06-18 22:16 715MSDN,C#2.0 变量基本点: 变量代表 ... -
effective hierarchy(一)之 基本概念(4)
2009-06-15 15:59 726MSDN,C#2.0: 一、名字空间和类型名称(nam ... -
effective hierarchy(一)之 基本概念(3)
2009-06-14 15:00 730MSDN,C#2.0: 一、签 ... -
effective hierarchy(一)之 基本概念(2)
2009-06-13 18:22 828MSDN:基本概念 一、 ... -
effective hierarchy(一) 之基本概念(1)
2009-06-11 09:37 778我告诫自己,不要忽略简单的东西,又要扎实。所以这一篇开始,专门 ... -
effective hierarchy(一)之 属性与索引器
2009-06-07 14:07 1149编程笺言:“优良的设计 ... -
effective hierarchy(一)之 从array说起(4)
2009-06-07 00:53 803回顾:上一节中,我们 ... -
effective hierarchy(一)之 从array说起(3)
2009-06-06 21:20 700回顾: 从上一节中,可以看出枚举的易用性对数组使用的启示意义 ... -
effective hierarchy(一)之 从array说起(2)
2009-06-06 09:34 691复习: 从上一节,可 ... -
effective hierarchy(一)之 从array说起(1)
2009-05-29 13:59 891MSDN(c#2.0): 数组的元义是 ... -
effective hierarchy(一)之 C#中的new
2009-05-27 23:21 840MSDN(c#2.0): 在msdn中,指出new的三种用法。 ...
相关推荐
在了解 IRQ hierarchy 的初始化及构建过程之前,需要先了解三个基本概念:IRQ、HWIRQ 和 GSI。 IRQ(Interrupt Request)是系统中的一种机制,允许外部设备请求 CPU 的注意,以便处理某些事件。IRQ 是一个抽象的...
Android 开发中,UI 分析工具 Hierarchy Viewer 是一个非常有用的工具,可以帮助开发者快速、方便地设计、调试和调整界面,提高用户的开发效率。本文将详细介绍如何使用 Hierarchy Viewer 工具在 Android 开发过程中...
Hierarchy PRO是一个高级的Hierarchy面板扩展,它提供了更多的自定义选项和便捷的功能。此插件可能包括但不限于以下特性: 1. **过滤与排序**:Hierarchy PRO允许用户根据需求筛选和排序场景中的游戏对象,比如按...
解决这个启动 View Hierarchy时的这个bug: hierarchy viewer unable to debug device
本篇文档将详细介绍如何为员工及其部门经理创建一个层次数据源(Hierarchy Extractor)。通过这个层次数据源,管理者可以方便地获取到他们直接下属的信息以及这些下属所负责或有权访问的数据。此文档适用于运行ECC 5.0...
Hierarchy Viewer是Android SDK中自带的一款强大的UI设计与检测工具。它位于Android SDK的`tools`目录下,文件名为`hierarchyviewer.bat`。对于Android开发者来说,这是一个不可或缺的工具,能够帮助他们在开发过程...
效果及使用方法:https://blog.csdn.net/qq_26318597/article/details/131373576 1.导入插件包 2.选中Hierarchy中的物体 3.右键选择“复制Hierarchy路径” 4.Ctrl + V 粘贴
#### 一、Hierarchy Viewer概览 Hierarchy Viewer是一款由Google提供的强大工具,专为Android开发者设计,旨在辅助界面设计、调试及优化工作流程。通过直观地展示应用UI的层级结构,它使得开发者能够轻松识别布局...
Unity是世界上最流行的游戏开发引擎之一,它为开发者提供了丰富的工具集来创建交互式3D和2D内容。在Unity中,Hierarchy面板是一个至关重要的组成部分,它显示了场景中的所有游戏对象及其层次结构。QHierarchy是一款...
Enhanced_Hierarchy_2.0_v2.4.5.unitypackage 最新版本哦 正版
Hierarchy PRO 2021 v2021.1u9
Unity是世界上最受欢迎的游戏开发引擎之一,它提供了丰富的工具和功能,让开发者能够创建高质量的2D和3D游戏。在Unity中,"层级编辑器"(Hierarchy)是至关重要的一个部分,它允许开发者组织和管理场景中的游戏对象...
在游戏开发领域,“演化你的游戏实体层级”(Evolve Your Hierarchy)这一概念强调了从传统的深度类层级结构向更灵活、可扩展的游戏实体组件化方法转变的重要性。这种方法不仅提高了代码的复用性与可维护性,还简化...
WPF Class Hierarchy
通过阅读和理解《VS2010 MFC Hierarchy Chart》的三个部分(MFC_Hierarchy_Chart1of3.pdf、MFC_Hierarchy_Chart2of3.pdf、MFC_Hierarchy_Chart3of3.pdf),开发者可以深入理解MFC的内部结构,从而更好地利用MFC开发...
Hierarchy2 v1.3.10.0
在这个名为“前端项目-d3-hierarchy.zip”的压缩包中,包含了一个使用D3.js库实现的层次数据可视化布局算法。D3.js(Data-Driven Documents)是一个强大的JavaScript库,专为创建动态、交互式的SVG、Canvas或WebGL...
7. **物理视图(Physical Views)**:设计的物理视图与电路模型相对应,用于布局布线和生成制造文件,如GDSII或Gerber格式。 在"ADS2011_Hierarchy.wmv"这个视频文件中,可能详细展示了如何在ADS 2011环境中创建和...