为什么语句是 (j++);禁止?

Why was the statement (j++); forbidden?

下面的代码是错误的(见on ideone):

public class Test
{
    public static void Main()
    {
        int j = 5;
        (j++);      // if we remove the "(" and ")" then this compiles fine.
    }
}

error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement

  1. 为什么删除括号后代码可以编译?
  2. 为什么编译不带括号?
  3. 为什么 C# 是这样设计的?

C# language specification

Expression statements are used to evaluate expressions. Expressions that can be used as statements include method invocations, object allocations using the new operator, assignments using = and the compound assignment operators, increment and decrement operations using the ++ and -- operators and await expressions.

在语句两边加上圆括号会创建一个新的圆括号表达式。来自规范:

A parenthesized-expression consists of an expression enclosed in parentheses. ... A parenthesized-expression is evaluated by evaluating the expression within the parentheses. If the expression within the parentheses denotes a namespace or type, a compile-time error occurs. Otherwise, the result of the parenthesized-expression is the result of the evaluation of the contained expression.

由于括号中的表达式未被列为有效的表达式语句,因此根据规范,它不是有效的语句。为什么设计师选择这样做是任何人的猜测,但我敢打赌,因为如果整个语句包含在括号中,括号就没有用处:stmt(stmt) 完全相同。

因为 i++ 周围的括号是 creating/defining 一个表达式.. 正如错误消息所说.. 一个简单的表达式不能用作语句。

为什么语言被设计成这样?为了防止错误,将误导性的表达式作为语句,不会像代码

那样产生副作用
int j = 5;
j+1; 

第二行没有效果(但你可能没有注意到)。但是编译器并没有删除它(因为不需要代码)。它明确要求您删除它(这样您就会知道错误)或者修复它以防您忘记输入内容。

编辑:

为了使括号部分更清楚.. c# 中的括号(除了其他用途,如强制转换和函数调用)用于对表达式进行分组,return 单个表达式(子表达式的构成)表达式)。

在该级别的代码中只允许使用 staments.. 所以

j++; is a valid statement because it produces side effects

但是通过使用括号,你将它变成了表达式

myTempExpression = (j++)

还有这个

myTempExpression;

无效,因为编译器无法确保表达式作为副作用。(并非没有引起暂停问题)..

Deep insights appreciated.

我会尽力的。

正如其他答案所指出的,这里发生的事情是编译器检测到 表达式 被用作 语句 。在许多语言中——C、JavaScript 和许多其他语言——将表达式用作语句是完全合法的。 2 + 2; 在这些语言中是合法的,即使这是一个无效的声明。有些表达式只对它们的值有用,有些表达式只对它们的副作用有用(比如调用返回 void 的方法),不幸的是,有些表达式对两者都有用。 (如递增。)

要点是:仅由表达式组成的语句几乎肯定是错误的除非这些表达式通常被认为比它们的值更能产生副作用。 C# 设计者希望通过允许通常被认为具有副作用的表达式,同时禁止那些通常也被认为对其值有用的表达式来找到中间立场。他们在 C# 1.0 中确定的表达式集是递增、递减、方法调用、赋值,以及有点争议的构造函数调用。


旁白:人们通常认为对象构造是用于它产生的值,而不是构造的副作用;在我看来,允许 new Foo(); 有点不当。特别是,我在导致安全缺陷的真实代码中看到了这种模式:

catch(FooException ex) { new BarException(ex); } 

如果代码很复杂,发现这个缺陷可能会出奇地困难。


因此,编译器会检测包含不在该列表中的表达式的所有语句。特别是,带括号的表达式被标识为 -- 带括号的表达式。它们不在 "allowed as statement expressions" 的列表中,因此不允许。

所有这些都是为C#语言的一个设计原则服务的。 如果您输入 (x++);,您可能做错了什么。这可能是 M(x++); 的拼写错误,或者只是一些东西。请记住,C# 编译器团队的态度不是“我们能想出一些方法来完成这项工作吗?”C# 编译器团队的态度是“如果合理的话代码看起来可能有错误,让我们通知开发人员”。 C# 开发人员喜欢这种态度。

现在,综上所述,实际上有一些奇怪的情况,其中 C# 规范 确实 暗示或直接声明不允许使用括号,但 C# 编译器仍然允许使用它们。在几乎所有这些情况下,指定行为与允许行为之间的微小差异是完全无害的,因此编译器编写者从未修复这些小错误。您可以在这里阅读有关内容:

Is there a difference between return myVar vs. return (myVar)?