如果(condition) continue; 或者 if (!condition) { ... }?(风格偏好)

6

我知道这是一个风格问题,因此标记为主观。我有一小段代码,其中包含两个嵌套条件。我可以用两种方式编写它,并且我想看看更有经验的开发人员认为它应该是什么样子。

风格1:

while (!String.IsNullOrEmpty(msg = reader.readMsg()))
{
    RaiseMessageReceived();
    if (parseMsg)
    {
        ParsedMsg parsedMsg = parser.parseMsg(msg);
        RaiseMessageParsed();
        if (processMsg)
        {
            process(parsedMsg);
            RaiseMessageProcessed();
        }
    }
}

样式2:

while (!String.IsNullOrEmpty(msg = reader.readMsg()))
{
    RaiseMessageReceived();
    if (!parseMsg) continue;

    ParsedMsg parsedMsg = parser.parseMsg(msg);
    RaiseMessageParsed();
    if (!processMsg) continue;

    process(parsedMsg);
    RaiseMessageProcessed();
}

(附带问题:我如何在源代码示例中添加空行?)


你在代码文本中留空行吗?如果所有的缩进都是4个空格(加上代码自身的缩进),那么对我来说一切都正常。 - Jonathan Leffler
@Jonathan:我尝试按照你描述的方式添加空格,但格式化程序仍然会吃掉它们! - Hosam Aly
我发现样式2更容易阅读和快速理解。 - sblom
22个回答

22

我更喜欢第一种风格-带有缩进。


1
通常只有在处理参数验证时,我才会选择样式2的路线。 - Greg D

14

我倾向于使用第二种风格 - 使用 continue 语句。


由于风格1的等效选项获得了7票,我认为没有理由对此进行负评。点赞。 - Adriano Varoli Piazza
在使用一个集成到工作应用程序中的“自定义代码编辑器”几年后——它不支持制表符键或固定宽度字体,更别提缩进了——看到几层花括号会让我做噩梦。在两个选项中,我必须选择#2。 - Trevel
风格1的缩进非常易于一眼跟踪,但同样的代码在箭头代码中会导致代码混乱,让人头痛。我对箭头代码的厌恶促使我使用了风格2。 - Dinah

8
原则上,我同意大多数人喜欢样式1。这也是Steve Mcconnell在《代码大全》中所支持的 - 表达你的意思,即如果你更关心条件为真,而假状态较少或不被优先考虑,则表达首选版本。
但实际上,我经常使用样式2,因为我喜欢先清除所有可能的错误/无效状态。当我摆脱了所有我不感兴趣的可能性后,我可以将核心代码写到例程的末尾,而不必不断地想着是否需要防范其他某些情况。基本上,我的态度是,先去掉杂草,然后安心地做真正的工作。

1
我认为麦康奈尔赞成取消缩进。 - Dustin Getz
请注意,style2可能会将循环内容提取到一个方法中,并且该方法使用早期返回。https://dev59.com/PXVD5IYBdhLWcg3wQZUg - Dustin Getz

5

这两个都是虚假的。不要把赋值放在条件表达式中。

(!String.IsNullOrEmpty(msg = reader.readMsg()))

你只是因为阅读器的怪异行为才这样做 - 为什么阅读器会给你一个非消息来表示阅读完成?下面是一个设计更好的替代方案:

while (reader.HasMessage())
{
  string msg = reader.GetMessage();
  HandleMessage(msg);
}

嗯,对我来说,这似乎是一种避免在 while 关键字之前和之后写两次赋值的方法。有更好的替代方案吗? - Hosam Aly
+1:不得不承认,在条件表达式内部进行赋值操作,给人一种“看我有多聪明”的傲慢感。 - Juliet
不一定是“我有多聪明”,而更可能是“库没有提供足够的接口”。感谢建议,大卫! - Hosam Aly

4

我肯定更喜欢第一个版本。当不被过度使用时,continue语句非常好用。

我认为这与多个返回语句类似。它们适用于守护条件,并在提高清晰度时具有很好的用处,但不应过度使用。

此外,在代码块中,一行上的两个空格应该为您插入一个换行符。


谢谢。我尝试使用两个空格,但代码格式化程序似乎会将它们吞掉。 - Hosam Aly

3

投票支持,因为我同意这段代码需要重构,尽管我不确定Style2是否是正确的答案。 - Jay Bazuzi

3
我希望以另一种方式重构这段代码。你的代码做了太多的事情!
1. 读取输入 2. 迭代 3. 发出通知 4. 解析(有条件!(附有通知!)) 5. 处理(有条件!(附有通知!))
我认为我们需要一些分离。我想探索:
- 将读取输入移入迭代器(`yield return`)中。 - 将条件移入策略模式中。

我一定会探索这个选项,将它与David B的答案合并,并实现IEnumerator<string>。我现在会阅读有关该模式的内容,但这是否会有相对较大的开销呢?(这个循环是应用程序的核心,需要尽可能快地运行。) - Hosam Aly
不要在没有分析数据的情况下进行优化。我的性能猜测通常是错误的;你的也可能是。另一个想法:如果你有一个需要大括号的代码块(因为它是多行的),那么你就有了重构的机会。要无情! - Jay Bazuzi

2
在所示的示例中,我会选择样式 1。如果我的方法足够大,嵌套成为问题(且没有更好的重构代码的方法),那么我将考虑样式 2。但对于仅显示的两个紧凑案例来说,绝对选择样式 1。

2

在我看来,样式1更加清晰易懂。并不是说Continue有什么问题,但正如之前有人所说的那样,缩进使得内容更容易理解。


2
我个人更喜欢第二种样式,原因如下:
  • 如果您在if后缩进但忘记大括号,可能会出现潜在的错误。

  • 您有更少的风险遇到浮动else问题,即对于多个嵌套的ifs,您可能会混淆一个给定的else属于哪个if。

  • 您可以避免过度缩进,从而导致代码超出页面范围。

当我编写代码时,我倾向于将continue放在单独的一行上;

if (!parseMsg) 
    continue;

将其设置为块级元素的原因是使其更加可见,并且更容易为其分配断点。


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