为什么是陈述(j ++);禁止?

时间:2015-12-22 18:24:10

标签: c# syntax expression language-design parentheses

以下代码有误(请参阅on ideone):

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

错误CS0201:仅分配,调用,递增,递减,等待和   新对象表达式可以用作语句

  1. 为什么在删除括号时编译代码?
  2. 为什么不用括号编译?
  3. 为什么C#是这样设计的?

3 个答案:

答案 0 :(得分:219)

  

深刻见解。

我会尽力而为。

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

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

ASIDE:人们通常认为物体结构用于产生的价值,而不是建筑物的副作用;在我看来,允许new Foo();有点不合时宜。特别是,我在实际代码中看到了这种模式导致了安全性缺陷:

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

如果代码很复杂,很难发现这个缺陷。

因此,编译器可以检测由不在该列表中的表达式组成的所有语句。特别是,带括号的表达式被标识为 - 带括号的表达式。它们不在"允许作为语句表达式"列表中,因此不允许使用它们。

所有这些都是为了满足C#语言的设计原则。 如果您输入(x++);,那么您可能做错了。这可能是M(x++);或其他一些问题的错字。请记住,C#编译器团队的态度不是" 我们能找到一些方法来实现这项工作吗?" C#编译器团队的态度是" 如果合理的代码看起来像是一个可能的错误,请告知开发人员"。 C#开发人员喜欢这种态度。

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

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

答案 1 :(得分:46)

C# language specification

  

表达式语句用于计算表达式。可以用作语句的表达式包括方法调用,使用new运算符的对象分配,使用=的赋值和复合赋值运算符,使用++和 - 运算符的增量和减量运算以及等待表达式。

在句子周围加上括号会创建一个新的所谓括号表达式。从规范:

  

括号表达式由括在括号中的表达式组成。 ...通过计算括号内的表达式来计算括号表达式。如果括号内的表达式表示名称空间或类型,则会发生编译时错误。否则,parenthesized-expression的结果是包含表达式的评估结果。

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

答案 2 :(得分:19)

因为i++周围的括号正在创建/定义一个表达式..因为错误消息说...一个简单的表达式不能用作语句。

为什么语言设计成这种方式?防止错误,将错误的表达式作为语句,不产生像代码那样的副作用

int j = 5;
j+1; 

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

修改

使关于bracked的部分更清晰.. c#中的括号(除了其他用途,如强制转换和函数调用),用于对表达式进行分组,并返回单个表达式(使用子表达式)。

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

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

但是通过使用bracked你将它变成表达式

myTempExpression = (j++)

和这个

myTempExpression;

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