在C#中,条件块后面的分号有什么作用?

32

最近在一个项目中,我发现了这段代码——我认为它是错误放置的:

if(condition)
{
   //Whatever...
};
请注意右括号后的分号。
有人知道这会有什么影响吗?
我认为它没有任何作用,但是本以为它会导致编译器错误。

2
你试过编译它吗?结果是什么? - Robert Columbia
31
这只是一行无用的代码结尾 - if(true) {};;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 会编译通过。 - stuartd
4
这是一句空洞的话,没有实际作用。 - Some programmer dude
3
请注意,如果您编写了if(condition);,那么编译器会发出警告,指出空语句可能是一个错误;这是因为对于初学者,if(condition); DoIt();似乎在有条件地运行DoIt(),但实际上是无条件地运行它。编译器的作者可能也会对if(condition){};做同样的事情,但这个错误更有可能是无害的,因为它不会改变程序的含义。 - Eric Lippert
3
注意,C#允许在类声明后添加一个分号,并忽略它。在这种情况下,这只是为了照顾习惯在类后面加分号的C++程序员;在C++中这是必须的。 - Eric Lippert
显示剩余3条评论
3个回答

34

这是一个简单的问题,有一个简单的答案,但我想补充一些相关内容。通常人们认为它什么也没做,特别是对于你提出的情况,分号是不必要的行终止符。

但是它背后的理由是什么?

实际上,这些空语句是允许的,用于像这样的语句:

 // Use an empty statement as the body of the while-loop.
while (Method())
        ;
我同意它没有任何作用。但是它可以帮助某些循环符合语言的语法要求,我认为这是人们应该从中理解的。就像其他人所说,我同意你可以移除它,我只是想强调为什么C#允许它。
进一步澄清
当您不需要执行操作但需要语句时,可以使用空语句。它只是将控制转移到语句的终点。它根本没有任何效果,纯粹是“语法糖”。
正如@PaulF所说,在上面的示例中,您可以使用空块({})代替。它完全有效并具有相同的效果。
再次强调,这完全取决于风格。您不需要它,但它肯定可以帮助您符合编码环境的任何规则。
常见用例(其中可以看到空语句)
- While循环的空主体(与我上面强调的情况相同)
void ProcessMessages()
{
    while (ProcessMessage())
        ; // Statement needed here.
}
  • goto语句(很少使用,但仍然有效)

    void F()
    {
        //...
        if (done) goto exit;
    //...
    exit:
        ; // Statement needed here.
    }
    

    来自MSDN

  • 类声明(感谢@EricLippert提供此内容)

  • class SomeClass
    {
        ...
    };
    
    请注意,在这种情况下,正如@EricLippert在评论部分中所述,这只是对C++程序员的一种礼貌,他们习惯于在类之后键入分号; C++要求这样做。 尽管通常情况下空语句的使用存在争议,主要因为它们可能带来的混淆,但在我看来,从语法上讲,它们在C#中有一定的用武之地。我们不能忘记C#是C ++的增量(这主要解释了在两乘二网格中使用#,即四个"+"符号)而且出于历史原因,允许使用空语句有助于过渡。

    2
    我认为在C#中,“作用域”比“块”更可取,因为花括号对于作用域的影响。隐式作用域+nop语句=不仅仅是样式问题,而是我的看法。你可能会被诱惑盲目地删除那个分号,然后惊讶地发现你刚刚引入了一个错误,因为下一条语句现在是隐式作用域。 - Mathieu Guindon
    1
    @PaulF:作为一个写过很多解析器的人,没有“简单性”这个论点可言;空语句并不能简化解析器。我的观点是,空语句是一种不应该被添加到C#或者C中的错误特性。所有空语句所做的就是产生一个机会,让人们在一个难以察觉的地方放置一个分号,从而改变程序的含义。 - Eric Lippert
    1
    实际上,有一个简单的理由来说明为什么不要有这个功能。解析器需要解析空语句,因此它变得更加复杂, 解析器还需要添加启发式方法来检测并警告关于空语句的错误用法。当错误用法超过正确用法时,你就拥有了一个应该被移除的糟糕功能。 - Eric Lippert
    5
    @MathieuGuindon: 不不不,"scope" 在 C# 中有一个非常具体的意义。作用域是程序文本中的一定区域,在该区域内可以通过其未经修饰的名称访问特定实体。正确的思考方式是,块语句引入了一个局部变量声明空间,而该声明空间内部局部变量的作用域该块的文本。这些东西显然是密切相关的,但它们在逻辑上是不同的,因此不要混淆它们。 - Eric Lippert
    1
    这也完全是猜测 - 但我相当确定,在选择Java和C#之间,空语句不会成为C++程序员在决定用于新项目的语言时最担心的问题。 - PaulF
    显示剩余15条评论

    17

    虽然这种方式似乎没有任何影响,但我不建议您以这种方式编写代码。

    如果您想在 ; 后面添加 elseelse if,那么编译将会失败。

    示例:

    if(5>1) {
      //whatever
    }; else {
      //whatever
    }
    

    这段代码无法通过编译(注意在else前面有一个;


    3
    你说的并没有错,但是你改变了问题的焦点。它编译失败是因为else找不到之前的if。是你加上了else,而不是原始问题的作者。问题是为什么分号在结尾处是被允许的,而不是是否有时候分号在中间会引起问题。 - Flater

    8
    这是Visual Studio将编译为空语句的有效语法,因为它只是一个语句终止符。您的代码将编译,多余的;不会成问题。
    如果您想要清理代码,可以将其删除,但保留它不会造成任何负面影响。
    希望这可以帮到您。

    2
    Visual Studio不会忽略它,C#编译器将把它编译为有效的空语句语法,这将导致与没有尾分号的if语句相同的IL生成。 - Aaron M. Eshbach
    @AaronM.Eshbach true; 每个 ; 都会发出一个 nop - Sebastian Hofmann
    3
    只有在关闭优化时才会发出nop指令。 - Eric Lippert
    “这只是一个语句终止符”,可能更准确一些……在C语言中,行与很多东西都没有太大关系。 - Shadow

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接