为什么省略花括号被认为是一种不好的做法?

188

为什么每个人都告诉我像这样编写代码是一种不良实践呢?

if (foo)
    Bar();

//or

for(int i = 0 i < count; i++)
    Bar(i);

我认为省略花括号的最大优点是,有时候使用花括号会造成代码行数翻倍。例如,下面是用C#绘制标签发光效果的一些代码:

using (Brush br = new SolidBrush(Color.FromArgb(15, GlowColor)))
{
    for (int x = 0; x <= GlowAmount; x++)
    {
        for (int y = 0; y <= GlowAmount; y++)
        {
            g.DrawString(Text, this.Font, br, new Point(IconOffset + x, y));
        }
     }
 }
 //versus
using (Brush br = new SolidBrush(Color.FromArgb(15, GlowColor)))
    for (int x = 0; x <= GlowAmount; x++)
        for (int y = 0; y <= GlowAmount; y++)
            g.DrawString(Text, this.Font, br, new Point(IconOffset + x, y));

您还可以获得链接 usings 的附加好处,而无需缩进数百万次。

using (Graphics g = Graphics.FromImage(bmp))
{
    using (Brush brush = new SolidBrush(backgroundColor))
    {
        using (Pen pen = new Pen(Color.FromArgb(penColor)))
        {
            //do lots of work
        }
    }
 }
//versus
using (Graphics g = Graphics.FromImage(bmp))
using (Brush brush = new SolidBrush(backgroundColor))
using (Pen pen = new Pen(Color.FromArgb(penColor)))
{
    //do lots of work
}

花括号最常见的使用是为了维护编程,并避免在原始if语句和其预期结果之间插入代码带来的问题:

if (foo)
    Bar();
    Biz();

问题:

  1. 想使用语言提供的更紧凑的语法有错吗?设计这些语言的人很聪明,我无法想象他们会加入一种总是不好使用的特性。
  2. 我们应该还是不应该编写代码让最低公共分母能够理解并且没有问题地与之工作?
  3. 还有其他的论点我错过了吗?

8
我同意你的看法。省略它们。句号。 - Andrei Rînea
77
谁会在2010年关心某个东西有多少行啊?现在的显示器都很宽、很便宜,而且分辨率很高!我的显示器是2048 x 1152,而且我有两个!可读性比节省2个垂直线更重要,因为你很容易引入难以发现的细微错误。 - user177800
56
显示器宽而便宜,但不高也不便宜。垂直空间比水平空间更为紧缺。 - Adam Ruth
38
把它们横过来 :) - Zachary Yates
17
为了避免像苹果在2014年2月发现的SSL漏洞那样出糗,哈哈。 - learnvst
显示剩余15条评论
52个回答

190

实际上,唯一让我遇到麻烦的时候是我在调试时注释了bar()函数:

if(foo)
  // bar();
doSomethingElse();

除此之外,我倾向于使用:

if(foo) bar();

这个方法可以处理上述情况。

编辑 感谢澄清问题,我同意我们不应该把代码写成最低公共分母。


34
当然,那很困难,但我认为对于维护者而言最大的问题是他们会添加第二行,并且意识不到它并不是条件语句的一部分,尽管它看起来真的应该是。 - danieltalsky
24
我不喜欢单行代码风格,因为我总是要往下寻找循环体。 - Nosredna
19
我认为单行风格比起有用更令人烦躁,因为(特别是在深度嵌套的情况下——呃),这样的语句很容易被忽略,导致混淆。但我几乎只在输入验证(即早期返回)或循环控制(例如,在文件系统遍历中忽略不相关的文件名)时使用这种单语句if,其中空行可以帮助将它们与主代码区分开来。 - Alan Plum
1
单行样式也会隐藏test coverage中的bar()。即使foo始终为false,它看起来也是被覆盖的。 - Bohumil Janda
1
这句话的另一种表达方式是,“如果源代码包含花括号,我在调试时就不会浪费时间了。”添加花括号非常简单,避免了给下一个人留下陷阱。唯一的反对意见是“可读性”,但考虑到如果省略它们,某些情况下会隐式发生某些事情,这种反对意见相当牵强。 - Andrew Theken
1
@AndrewTheken 这并不是说“如果我在解析代码方面变得更好,我就不必担心这个问题了……”的方式。即使在调试过程中,省略 '{}' 也从未给我带来过问题。它看起来也更美观。加上输入它们所需的时间(尤其是如果在不同行上),它浪费的时间比节省的时间还要多。 - Pryftan

162

阅读速度...

除了已经提到的内容之外,此时我已经习惯了解析带有大括号和空格的if语句。所以我会这样阅读:

if (condition)
{
    DoSomething();
}

DoSomethingElse();

比我阅读速度稍快:

if (condition) DoSomething();

DoSomethingElse();

如果它是这个样子,我会读的稍微慢一些:

if (condition) DoSomething();
DoSomethingElse();

我读这个比之前慢很多:

if (condition) 
    DoSomething();
DoSomethingElse();

因为我不禁会再次阅读它,以防万一并思考作者是否有意

if (condition)
{
    DoSomething();
    DoSomethingElse();
}

虽然已经在通用部分涵盖过,但是当涉及到阅读以下内容时,我将会花很长时间来确保作者的意图。我甚至可能会追踪原作者以确认。

if (condition) 
    DoSomething();
    DoSomethingElse();

30
当您在第四个示例中,在if语句后面加一个空行以将其与DoSomethingElse()分开时,问题就解决了。这正是我所做的,可读性实际上与第一示例相同(如果不是更好的话;-P)。另一件事是,将代码行分组成2或3行显着有助于阅读性,您可以更快地浏览代码。 - Piotr Owsiak
7
也许是因为我习惯了Python,所以把第一个花括号放在if语句的同一行能让我更快地阅读和理解它。 - Ponkadoodle
18
没有括号的编码风格存在的问题在于,它会浪费你宝贵的注意力去思考这个代码块是否有括号,而不是更重要的事情。仅此一点就足以使用最简单的惯例,即始终使用括号作为代码块的标记。 - Nate C-K
3
括号的性质本来就是明确的,我会坚持使用它,谢谢。 - TheOptimusPrimus
3
在最后一种情况下,追查原作者并惩罚他... ;) - Per Lundberg
显示剩余6条评论

55

如果是一些小东西,可以像这样写:

if(foo()) bar();

如果字数够长而需要分成两行,使用大括号。


是的,这也是我做的。如果你添加另一行,它会强制你添加大括号,很容易看出bar()实际上是if的一部分,如果你只是将其注释掉,那么坏事就会发生。 - jalf
12
虽然不太友好于调试器。但是,您如何在“bar”部分设置断点? - Nemanja Trifunovic
11
把光标放在"bar();"上,然后按 F9 键。无论是 VS2005 还是 VS2008,在行内断点处理方面都非常出色,只要这些断点是单独的“语句”。 - GalacticCowboy
4
GalacticCowboy: 有比你所知道的环境更多。 :-) - user14070
20
可以,但鉴于上下文是关于C#的,这应该覆盖了绝大多数用户... - GalacticCowboy
显示剩余2条评论

50

我曾经也认为只有在必要的时候才使用大括号更好。但现在不是了,主要原因是当你有很多代码时,使用一致的大括号风格可以使代码更易读,并且你可以更快地浏览代码。

除了有人向if语句添加第二个语句之外,始终使用大括号的另一个好处是避免类似以下情况的发生:

if(a)
   if(b)
     c();
else
   d();

你是否注意到else子句实际上是“if(b)”的子句?你可能已经注意到了,但你会相信任何人都熟悉这个陷阱吗?

因此,为了保持一致性,因为你永远不知道当别人(总是别人愚蠢)更改代码时可能会发生什么意外情况,我总是加入大括号,因为它使源代码更易读,更快速地被大脑解析。只有对于最简单的if语句,比如进行委托或类似开关的if语句,在这种情况下,你知道子句永远不会被扩展,我才会省略大括号。


13
这是我使用大括号的两种情况之一,否则不会使用。 - Andrei Rînea
3
我认为这句话应该被改写为 if (a && b) ... ,以使其更易于理解。 - alexander.biskop
8
当加入大括号时,!a || !b 的含义与无括号 (!a) 或有且仅有一组括号 (a && !b) 是不同的。 - Ben Voigt
1
@Ben Voigt:没错!我上当了;-)半年和两个赞之后,有人意识到我所说的话是错误的...猜想我没有足够地关注细节,主要是因为我评论背后的意图与指出那个if语句的技术正确转换不同。我的意思是,嵌套的内联if语句通常会显著降低可读性(尤其是控制流的可追踪性),因此应该将它们合并为一个语句(或完全避免使用)。干杯 - alexander.biskop
3
同时,使用嵌套的if语句且条件越简单,则调试会更加容易,因为您可以逐步执行每一个条件语句。 - Ben Voigt
显示剩余2条评论

38

代码行数很便宜。 处理器性能很便宜。 开发人员的时间非常昂贵。

通常情况下,除非我正在开发某些绝对需要资源/速度的应用程序,否则我总是倾向于编写以下代码:

(a)易于让任何其他开发人员跟随我的步伐

(b)对可能需要注释的代码部分进行特定标注

(c)如果出现问题容易调试

(d)如果需要在将来进行修改(即添加/删除代码)易于修改

从业务角度来看,代码的速度或学术优雅性次于这些因素。这并不是说我会故意编写笨重或丑陋的代码,但这是我的优先顺序。

大多数情况下省略花括号会使(b),(c)和(d)更加困难(注意不是不可能)。 我认为使用花括号与否对(a)没有影响。


9
你关于(a)的最后一句话是一个观点,包括其他在这里发帖的开发人员在内的许多人都会持不同意见。 - Kelly S. French

35

这并不总是被认为是一种不好的做法。Mono 项目编码指南建议如果没有必要就不要使用花括号。GNU 编码标准也是如此。我认为这始终是编码标准中个人口味的问题。


3
在阅读了整个指南之后,看Mono代码对我来说似乎很陌生。 - dance2die
哦,我的天啊,那个GNU页面也有一些奇怪的东西。搜索“对于函数体,我们推荐的样式看起来像这样”。我以前从未见过大括号和缩进结合在一起的样式,我真希望我再也不会看到这种样式了! - Isti115

34

我更喜欢花括号带来的清晰度。你可以准确地知道意思,不必猜测是否有人犯了错误而留下了它们(并引入了一个bug)。唯一省略它们的时间是当我把if和操作放在同一行时。我也不经常这样做。实际上,我更喜欢通过将花括号放在自己的行上引入的空格,尽管从多年的K&R类C编程中来看,如果IDE不为我实施该实践,我就必须努力克服以结束该行。

if (condition) action();  // ok by me

if (condition) // normal/standard for me
{
   action();
}

23

我认为这取决于你所工作的项目的指导方针和个人口味。

当它们不需要时,我通常会省略它们,除非像以下情况一样:

if (something)
    just one statement; // i find this ugly
else
{
    // many
    // lines
    // of code
}

我更喜欢

if (something)
{
    just one statement; // looks better:)
}
else
{
    // many
    // lines
    // of code
}

15

直白地说,我认为:

好的程序员会进行防御性编程,坏的程序员则不会。

由于上面有几个示例以及我自己与遗忘大括号有关的错误经验,我通过艰难的方式学到了始终要在代码中加入大括号。

其他任何选择个人风格而不是安全的行为都是错误的编程。

Joel甚至在Making Wrong Code Look Wrong中提到了这一点。

一旦你因为漏掉大括号而被一个bug所困扰,你就会知道漏掉大括号看起来很奇怪,因为你知道它是另一个bug可能发生的地方。


如果大括号单独放在一行上,那么任何两行或多行缩进但没有大括号对的地方看起来都会很奇怪,任何未匹配的大括号对也是如此。这种用法会在 if 语句控制一个块的情况下浪费一行空白,但在 if 语句控制单个操作而不是块的情况下避免了需要块的情况。 - supercat

15

可能会出现这种情况的其中一个实例是在 C/C++ 宏的旧时代。我知道这是一个关于 C# 的问题,但通常编码标准会延续下来而不解释为什么首先创建该标准。

如果你在创建宏时不够小心,可能会导致 if 语句中没有使用 {} 的问题。

#define BADLY_MADE_MACRO(x) function1(x); function2(x);

if (myCondition) BADLY_MADE_MACRO(myValue)

现在,别误会我的意思,我并不是说你应该总是在 C/C++ 中使用 {} 以避免这个问题,但因为这个问题,我不得不处理一些非常奇怪的 bug。


2
如果所有的代码都能自我声明就好了... 叹气 - Nathan Strong

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