下面的代码是错误的(参见 准备好了) :
public class Test { public static void Main() { int j = 5; (j++); // if we remove the "(" and ")" then this compiles fine. } }
错误 CS0201: 只有赋值、调用、增量、减量、等待和 新的对象表达式可以用作语句
在 C # 语言规范
表达式语句用于计算表达式。可以用作语句的表达式包括方法调用、使用 new 运算符的对象分配、使用 = 和复合赋值运算符的赋值、使用 + + 和——运算符的递增和递减操作以及等待表达式。
在语句周围加上圆括号将创建一个新的所谓的圆括号表达式:
括号化表达式由括在括号中的表达式组成。... 括号内的表达式通过计算括号内的表达式来计算。如果括号内的表达式表示命名空间或类型,则会发生编译时错误。否则,括号化表达式的结果是对包含的表达式求值的结果。
由于括号内的表达式不作为有效的表达式语句列出,因此根据规范,它不是有效的语句。为什么设计者选择这样做是任何人的猜测,但我打赌是因为括号没有有用的工作,如果整个语句包含在括号: stmt和 (stmt)是完全相同的。
stmt
(stmt)
因为 i++周围的括号正在创建/定义一个表达式。.正如错误消息所说。.一个简单的表达式不能用作语句。
i++
为什么语言被设计成这样?为了防止错误,使用误导性的表达式作为语句,不会产生像使用代码那样的副作用
int j = 5; j+1;
第二行没有效果(但是你可能没有注意到)。而不是编译器删除它(因为不需要代码)。它明确地要求您删除它(这样您就会意识到或错误)或修复它,以防您忘记键入的东西。
编辑 :
让关于布莱克的部分更加清晰。.C # 中的括号(除了其他用途,如 cast 和函数调用) ,用于对表达式进行分组,并返回单个表达式(由子表达式组成)。
在那个级别的代码中只允许使用基元. . 所以
j++; is a valid statement because it produces side effects
但是通过使用括号,你把它变成了表达式
myTempExpression = (j++)
还有这个
myTempExpression;
是无效的,因为编译器不能测量表达式作为副作用。
谢谢你的深刻见解。
我会尽力的。
正如其他答案所指出的,这里发生的是编译器检测到 表情被用作 声明。在许多语言中—— C、 JavaScript 和许多其他语言——使用表达式作为语句是完全合法的。2 + 2;在这些语言中是合法的,即使这是一个没有效果的语句。有些表达式只对它们的值有用,有些表达式只对它们的副作用有用(比如调用 void 返回方法) ,不幸的是,有些表达式对两者都有用。(比如增量)
2 + 2;
重点是: 只包含表达式的语句几乎肯定是错误 除非这些表达式通常被认为对它们的副作用比它们的值更有用。C # 设计人员希望找到一个中间立场,允许那些通常被认为是副作用的表达式,同时不允许那些通常被认为对其值有用的表达式。他们在 C # 1.0中标识的表达式集是增量、减量、方法调用、赋值,还有一些有争议的构造函数调用。
旁白: 人们通常认为一个对象构造是为了它所产生的价值而使用的,而不是为了构造的副作用; 在我看来,允许使用 new Foo();是一个错误的特性。特别是,我在现实世界的代码中看到过这种模式,它导致了一个安全缺陷:
new Foo();
catch(FooException ex) { new BarException(ex); }
如果代码很复杂,那么发现这个缺陷可能会非常困难。
因此,编译器将检测所有由不在列表中的表达式组成的语句。特别是括号化的表达式被标识为括号化的表达式。它们不在“允许作为语句表达式”的列表中,因此不被允许。
所有这些都是为 C # 语言的设计原则服务的。如果你输入 (x++);,你可能做错了什么.这可能是 M(x++);的打印错误或者其他什么的。记住,C # 编译器团队的态度不是“ 我们能不能想办法解决这个问题?”,C # 编译器团队的态度是“ 如果貌似可信的代码看起来像一个可能的错误,让我们通知开发人员”。C # 开发人员喜欢这种态度。
(x++);
M(x++);
现在,说了这么多,实际上有一些奇怪的情况,C # 规范 是的暗示或声明不允许使用括号,但 C # 编译器还是允许使用括号。在几乎所有这些情况下,指定行为和允许行为之间的微小差异是完全无害的,因此编译器编写者从未修复这些小错误。你可以在这里读到:
返回 myVar 和返回(myVar)之间有区别吗?