为什么说(j ++); 禁止?
下面的代码是错误的(见ideone ):
public class Test { public static void Main() { int j = 5; (j++); // if we remove the "(" and ")" then this compiles fine. } }
错误CS0201:只能使用赋值,调用,递增,递减,等待和新对象expression式作为语句
- 为什么当我们删除括号的时候编译代码?
- 为什么不用括号编译?
- 为什么C#是这样devise的?
深深的见解表示赞赏
我会尽力的。
正如其他答案已经指出,这里发生的是编译器正在检测到一个expression式被用作一个语句 。 在许多语言中,C,JavaScript和其他许多语言,使用expression式作为语句是完全合法的。 2 + 2;
在这些语言中是合法的,尽pipe这是一个无效的陈述。 有些expression式只对它们的值有用,有些expression式只对副作用有用(比如对void返回方法的调用),有些expression式对于两者都是有用的。 (像增量一样)
要点是:只包含expression式的语句几乎肯定是错误的, 除非这些expression式通常被认为对它们的副作用比它们的值更有用 。 C#devise师希望find一个中间立场,允许通常被认为是副作用的expression,而不允许那些通常被认为对他们的价值有用的expression。 他们在C#1.0中定义的expression式集合是增量,递减,方法调用,赋值以及有争议的构造函数调用。
除此之外,人们通常认为一个物体的build造是为了它所产生的价值,而不是为了build筑的副作用。 在我看来允许new Foo();
是一个错位。 特别是,我在真实世界的代码中看到了这种导致安全缺陷的模式:
catch(FooException ex) { new BarException(ex); }
如果代码复杂,可能很难发现这个缺陷。
因此,编译器将检测由不在该列表中的expression式组成的所有语句。 特别地,括号expression式被确定为括号expression式。 它们不在“允许作为语句expression式”的列表中,因此它们被禁止。
所有这些都是服务于C#语言的devise原则。 如果你键入(x++);
你可能做错了什么 。 这可能是M(x++);
的拼写错误M(x++);
或者一些正义的事情。 请记住,C#编译器团队的态度不是“ 我们能想出一些方法来实现这个目标吗? ”C#编译器团队的态度是“ 如果可能的代码看起来像是一个可能的错误,那么让我们通知开发者 ”。 C#开发者喜欢这种态度。
现在,所有这些说实际上有一些奇怪的情况,C#规范确实暗示或明确表示圆括号是不允许的,但C#编译器允许它们。 在几乎所有这些情况下,指定的行为和允许的行为之间的微小差异是完全无害的,因此编译器作者从未修复过这些小错误。 你可以在这里阅读关于这些:
返回myVar与返回(myVar)之间有区别吗?
在C#语言规范中
expression式语句用于评估expression式。 可用作语句的expression式包括方法调用,使用new运算符的对象分配,使用=的赋值和复合赋值运算符,使用++和 – 运算符的增量和减量操作以及awaitexpression式。
将括号括在一个语句中会创build一个新的所谓的带括号的expression式。 从规格:
括号内的expression式由括在括号内的expression式组成。 …括号内expression式通过评估括号内的expression式来评估。 如果括号内的expression式表示命名空间或types,则会发生编译时错误。 否则,括号expression式的结果就是包含expression式的评估结果。
由于带括号的expression式不被列为有效的expression式语句,所以根据规范它不是一个有效的语句。 为什么devise师select这样做是任何人的猜测,但是我敢打赌,如果整个陈述包含在圆括号中,括号没有任何有用的工作: stmt
和(stmt)
完全一样。
因为围绕i++
的括号正在创build/定义一个expression式..作为错误消息说..一个简单的expression式不能用作一个语句。
为什么这个语言被devise成这样呢? 以防止错误,误导expression式作为语句,不会产生像代码一样的副作用
int j = 5; j+1;
第二行不起作用(但你可能没有注意到)。 但不是编译器将其删除(因为代码是不需要的),它明确要求你删除它(所以你会知道或错误)或修复它,以防万一你忘记input的东西。
编辑 :
使关于bracked的部分更加清晰。c#中的括号(除了其他用途,如cast和函数调用),用于对expression式进行分组,并返回单个expression式(构造子expression式)。
在这个级别的代码只允许staime允许..所以
j++; is a valid statement because it produces side effects
但是通过使用扼杀,你把它变成expression
myTempExpression = (j++)
和这个
myTempExpression;
是无效的,因为编译器不能确定expression式的副作用(不是没有陷入暂停问题)。