为什么要声明(j ++);禁止的?


169

以下代码是错误的(请参阅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#是这样设计的?

29
@Servy涉及C#语言设计的很多人都在讲,所以这就是为什么我问。这有什么问题吗?
user10607

34
除了“为什么一种特定的编程语言为什么以这种方式处理这种特定的行为?”,我无法想象一个更具话题性的问题。
法师Xy

20
现在我们有了Eric Lippert的答案。也许您想重新考虑关于已接受答案的决定。现在是圣诞节,所以也许您为时过早。
Thomas Weller

17
@Servy您是在暗示设计决策永远不会被记录在案,并且对其他所有人是绝对未知的吗?他不是在要求SO用户猜测,而是在寻找答案-这并不意味着他是在猜测。人们是否以猜测来回答就在他们身上,而不是他。而作为OP指出的那样,人们对堆栈溢出谁实际上是在C#工作,取得了这些决定。
罗布

5
@Rob:问题是,是的,除非已经在某个地方记录了基本原理,否则必须这样做,但是猜测很有趣,谁不想分享自己的想法?如果您找到一种方法可以确保可靠地剔除这种纯粹(或“受过教育”的)猜测,请告诉我,我们将发大财。
Deduplicator 2015年

Answers:


221

深刻的见解表示赞赏。

我会尽力的

正如其他答案所指出的那样,这里发生的是编译器正在检测表达式正在用作语句。在许多语言(C,JavaScript和许多其他语言)中,使用表达式作为语句是完全合法的。 2 + 2;即使这些陈述无效,在这些语言中也是合法的。一些表达式仅对它们的值有用,某些表达式仅对它们的副作用(如对void返回方法的调用)有用,而不幸的是,某些表达式对它们都有用。(如增量。)

要点是:仅由表达式组成的语句几乎可以肯定是错误,除非通常认为这些表达式在副作用方面比其值更有用。C#设计师希望通过允许通常被认为是副作用的表达式,而不允许那些通常被认为对其值有用的表达式来找到中间立场。他们在C#1.0中标识的表达式集是增量,减量,方法调用,赋值,以及在构造程序调用中引起争议的一组表达式。


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

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

如果代码很复杂,则很难发现此缺陷。


因此,编译器将检测包含不在该列表中的表达式的所有语句。特别是,带括号的表达式仅被标识为带括号的表达式。它们不在“允许作为语句表达式”列表中,因此是不允许的。

所有这些都符合C#语言的设计原则。如果您键入(x++);,可能是您做错了什么。这可能是错别字M(x++);或只是一些事情。记住,C#编译器团队的态度不是“ 我们能找到某种方法使这项工作吗? ” C#编译器团队的态度是“ 如果看似合理的代码看起来像是可能的错误,那就通知开发人员 ”。C#开发人员喜欢这种态度。

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

return myVar与return(myVar)之间有区别吗?


5
回复:“通常认为构造函数调用用于它产生的值,而不是构造的副作用;在我看来,这有点不妥当”:我以为这个决定中有一个因素是,如果编译器禁止它,就不会有在你案件的任何清洁修复调用它的副作用。(大多数其他禁止的表达式语句具有无关紧要的多余部分,可以删除,除了... ? ... : ...,使用的是if/ else代替。)
ruakh 2015年

1
@ruakh:由于不应该这样做,因此,只要有一个合理的廉价/简单修复程序,就不必进行彻底修复。哪有 将其分配给变量,不要使用该变量。如果您想公然使用它,请将该行代码作为自己的作用域。尽管从技术上讲,这可能会改变代码的语义(无用的引用分配仍然是无用的引用分配),但实际上这不太重要。我承认它的确会产生稍微不同的IL ...但是仅当编译时没有优化时才如此。
布赖恩

键入时,Safari,Firefox和Chrome都提供ReferenceError contineu;
Brian McCutchon

2
@McBrainy:你是对的。我误记了拼写错误造成的缺陷;我会检查我的笔记。同时,我已经删除了令人反感的语句,因为它与此处的大点无关。
埃里克·利珀特

1
@迈克:我同意。我们有时会看到该语句的另一种情况是测试用例,try { new Foo(null); } catch (ArgumentNullException)...但显然,根据定义,这些情况不是生产代码。可以编写这样的代码以分配给虚拟变量似乎是合理的。
埃里克·利珀特

46

C#语言规范中

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

在语句周围加上括号会创建一个新的称为括号的表达式。从规格:

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

由于没有将带括号的表达式列为有效的表达式语句,因此根据规范,它不是有效的语句。为何设计人员选择采用这种方式,这是每个人的猜测,但我敢打赌,因为如果整个语句都包含在括号中stmt(stmt)则括号没有任何用处:并且完全相同。


4
@Servy:如果您仔细阅读,它的细微之处就不止于此。所谓的“表达式语句”确实是有效语句,并且该规范列出了可以是有效语句的表达式类型。但是,我从规范中重复了一次,即称为“括号表达式”的表达式类型不在有效表达式列表中,该列表可以用作有效表达式语句。
Frank Bryce 2015年

4
OP被询问任何关于语言设计。他只是想知道为什么这是一个错误。答案是:因为它不是有效的声明
Leandro

9
好,我不好 答案是:因为按照规范,这不是有效的声明。在那里,这就是设计的原因!
Leandro

3
问题不是要问一个深层的,由设计驱动的原因,即为什么该语言被设计为以这种方式处理这种语法-他是在问为什么一个代码段在另一段代码(对于初学者来说应该表现得很正常)时进行编译同样)无法编译。这篇文章回答了。
法师Xy

4
@Servy JohnCarpenter不只是说“你不能那样做”。他是在说,您被混淆的两件事并不相同。j ++是一个Expression语句,而(j ++)是一个带括号的表达式。突然之间,OP现在知道区别和区别。那是一个很好的答案。它回答了他的问题的正文而不是标题。我认为,很多问题都来自问题标题中的“设计”一词,但这并不是设计人员必须回答的,而只是从规范中得出。
thinklarge 2015年

19

因为括号在 i++创建/定义表达式 ..如错误消息所述..简单的表达式不能用作语句。

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

int j = 5;
j+1; 

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

编辑

为了使有关括号的部分更清楚。c#中的方括号(除了其他用途,如cast和function调用)用于对表达式进行分组,并返回单个表达式(由子表达式组成)。

在该级别的代码中,仅允许使用台词。

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

但是通过使用括号,您可以将其变成表达式

myTempExpression = (j++)

还有这个

myTempExpression;

是无效的,因为编译器无法确保该表达式具有副作用。(并非不引起停顿问题)。


是的,那也是我最初的想法。但这确实有副作用。它执行j变量的后递增吗?
user10607

1
j++;是有效的声明,但是使用方括号则表示let me take this stement and turn it into an expression..,并且
表达式当时

太糟糕了,没有“计算并忽略”语句的形式,因为在某些情况下,代码可能希望断言可以在不抛出异常的情况下计算出一个值,但并不关心这样计算出的实际值。计算和忽略语句不会经常使用,但是在这种情况下会使程序员的意图明确。
supercat 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.