【问题标题】:What to put in the IF block and what to put in the ELSE block?在 IF 块中放入什么,在 ELSE 块中放入什么?
【发布时间】:2008-10-03 12:21:24
【问题描述】:

这是一个较小的风格问题,但您添加到代码中的每一点可读性都很重要。

如果你有:

if (condition) then
{
   // do stuff
}
else
{
   // do other stuff
}

你如何决定是这样还是这样更好:

   if (!condition) then
   {
     // do other stuff
   {
   else
   {
     // do stuff
   }

我的启发是:

  1. 保持条件为正(小于 阅读时心算)
  2. 将最常用的路径放入 第一个区块

【问题讨论】:

  • 我添加了“function-exit”标签,因为这与“多个退出点 - 好或坏”问题有关。
  • 我觉得这个问题很重要。恕我直言,忽略这个问题是臭名昭著(而且太普遍)“意大利面条代码”的主要原因之一......

标签: coding-style function-exit


【解决方案1】:

我更喜欢把最常见的路径放在第一位,而且我坚信减少嵌套,所以我会尽可能中断、继续或返回而不是 elsing。我通常更喜欢针对正面条件进行测试,或者将 [和名称] 负面条件反转为正面。

if (condition)
    return;

DoSomething();

我发现,通过大幅减少 else 的使用,我的代码更具可读性和可维护性,而且当我必须使用 else 时,它​​几乎总是一个更结构化的 switch 语句的绝佳候选者。

【讨论】:

  • 我通常会尝试将自己限制为每个函数一个返回语句。过早的退出会给下一个人带来各种混乱。
  • 只有当函数太长时,多次返回才是真正的问题。除非那个人没有正确理解代码,否则使用简短的函数额外的返回不应该引起混乱:)
  • 我非常同意这一点——每个函数只有一个退出点。它迫使你更明确地构建你的代码
  • 多重返回(及其夸张的return null)是现代形式的goto ...
  • 我曾经做过一些事情,比如在函数顶部附近声明一个返回变量,然后使用 if/else 逻辑,所以最后一行是返回该变量,但我进入的代码库没有采取这种方法。在很短的时间内,我逐渐习惯了它并意识到它非常容易理解。
【解决方案2】:

两个(矛盾的)教科书引述:

放置 if/else 的最短子句 在上面

--Allen Holub,“足以射中自己脚的绳索”,第 52 页

把普通情况放在 if 后面而不是 else 后面

--史蒂夫·麦康奈尔,“代码完成,第 2 版。”,p356

【讨论】:

  • 如果两个子句都不是很长,Holub 的观点就变得毫无意义。这很容易实现。只需将代码提取到一个新函数中即可。
【解决方案3】:

我更喜欢第一个。条件应该尽可能简单,并且应该相当明显哪个更简单 out of condition 和 !condition

【讨论】:

  • 我稍微澄清了这个问题。 condition 和 !condition 不会造成太大的麻烦,但有时它们会更长,更复杂,有多个 ands 和 ors。
  • 在这种情况下,将条件封装在一个函数中。 (参见 Robert Martin 的清洁代码,第 301 页)
【解决方案4】:

这取决于您的流程。对于许多函数,我将使用前置条件:

bool MyFunc(variable) {
    if (variable != something_i_want)
        return false;

    // a large block of code
    // ...
    return true;
}

如果我需要每种情况,我将使用if (positive_clause) {} else {} 格式。

【讨论】:

    【解决方案5】:

    如果代码是为了检查错误情况,我更喜欢把那个代码放在第一位,“成功”的代码放在第二位;从概念上讲,这将函数调用及其错误检查代码保持在一起,这对我来说很有意义,因为它们是相关的。例如:

      if (!some_function_that_could_fail())
      {
         // Error handling code
      }
      else
      {
         // Success code
      }
    

    【讨论】:

      【解决方案6】:

      我同意 Oli 的观点,即尽可能使用肯定的 if 子句。

      请不要这样做:

      if (somePositiveCondition)
      else {
          //stuff
      }
      

      我曾经在我工作的一个地方经常看到这种情况,并且曾经怀疑其中一位编码员是否不明白如何不工作......

      【讨论】:

      • 我的猜测是'if'子句应该记录一个错误,但编码器忘记实现了。
      • 它在整个代码中发生了太多次,不可能是意外。
      【解决方案7】:

      当我查看数据验证时,我尝试将我的条件设为“白名单”——也就是说,我测试我会接受的内容:

      if DataIsGood() then
         DoMyNormalStuff
      else
         TakeEvasiveAction
      

      而不是反过来,它往往会退化为:

      if SomeErrorTest then
        TakeSomeEvasiveAction
      else if SomeOtherErrorCondition then
        CorrectMoreStupidUserProblems
      else if YetAnotherErrorThatNoOneThoughtOf then
        DoMoreErrorHandling
      else
        DoMyNormalStuff
      

      【讨论】:

        【解决方案8】:

        我知道这不正是您要寻找的,但是...许多开发人员使用“保护子句”,即否定的“if”语句,一旦该方法中断可能的。那时,真的没有“其他”。

        例子:

        if (blah == false)
        {
            return; // perhaps with a message
        }
        
        // do rest of code here...
        

        那里有一些铁杆 c/c++/assembly 家伙会告诉你,你正在破坏你的 CPU !!! (在许多情况下,处理器倾向于“真”语句并尝试“预取”接下来要做的事情......所以理论上任何“假”条件都会刷新管道并且会慢几微秒)。

        在我看来,我们正处于“更好”(更易于理解)的代码在 CPU 时间上胜出几微秒的地步。

        【讨论】:

        • CPU 不“偏爱‘真’语句”,它们使用分支预测,并且在某些情况下投机地执行两个分支。
        【解决方案9】:

        我认为对于单个变量,not 运算符足够简单,并且命名问题开始变得更加相关。

        永远不要将变量命名为 not_X,如果需要,请使用同义词库并找到相反的变量。我见过很多糟糕的代码,比如

        if (not_dead) {
        } else {
        }
        

        而不是显而易见的

        if (alive) {
        } else {
        }
        

        然后就可以正常使用了(可读性很强,代码块不用倒置)

        if (!alive) {
        } else {
        }
        

        如果我们谈论更多变量,我认为最好的规则是简化条件。一段时间后,项目往往会出现以下情况:

        if (dead || (!dead && sleeping)) {
        } else {
        }
        

        翻译成

        if (dead || sleeping) {
        } else {
        }
        

        始终注意条件是什么样的以及如何简化它们。

        【讨论】:

          【解决方案10】:

          软件是知识获取。您正在编码某人关于如何做某事的知识。

          软件应该适合问题的“自然”。如有疑问,请询问其他人,看看他们实际所说和所做的。

          “常见”情况是什么都不做的情况呢?然后呢

          if( common ) {
              // pass
          }
          else {
              // great big block of exception-handling folderol
          }
          

          或者你这样做?

          if( ! common ) {
              // great big block of except-handling folderol
          }
          

          “始终积极”的规则并不是您首先想要的。您想查看更多类似以下的规则。

          1. 总是自然的 - 它应该读起来像英语(或您组织中的任何通用语言。)
          2. 在可能的情况下,优先考虑常见情况 - 以便它们看起来很常见。
          3. 尽可能使用正逻辑;否定逻辑可以用在通常这样说的地方,或者常见的情况是什么都不做的地方。

          【讨论】:

            【解决方案11】:

            如果两条路径中的一条很短(1 到 10 行左右),而另一条则更长,我会按照Holub rule mentioned here 并将较短的一段代码放入 if。这使得在查看代码时更容易在一个屏幕上查看 if/else 流程。

            如果这不可能,那么我会构造以使条件尽可能简单。

            【讨论】:

              【解决方案12】:

              对我来说这取决于条件,例如:

              if (!PreserveData.Checked)
              {  resetfields();}
              

              我倾向于用我想要的逻辑与自己交谈,并将其编码成我脑海中的小声音。

              【讨论】:

              • “但是警官!声音告诉我要这样做……”很高兴听到我不是唯一这样做的人……
              【解决方案13】:

              您通常可以在不切换 if / else 块的情况下使条件为正。

              改变

                 if (!widget.enabled()) {
                   // more common 
                 } else {
                   // less common 
                 }
              

                 if (widget.disabled()) {
                   // more common 
                 } else {
                   // less common
                 }
              

              【讨论】:

                【解决方案14】:

                Intel Pentium 分支预测为“if”情况预取指令。如果它改为跟随“else”分支:它会刷新指令流水线,导致停顿。

                如果您非常关心性能:将最可能的结果放在“if”子句中。

                我个人是这样写的

                if (expected)
                {
                   //expected path
                }
                else
                {
                   //fallback other odd case
                } 
                

                【讨论】:

                • 我也倾向于遵循这个经验法则。
                【解决方案15】:

                如果您同时具有真假条件,那么我会选择肯定条件 - 这可以减少混淆,而且我相信总体而言,我相信您的代码更易于阅读。

                另一方面,如果您使用 Perl 等语言,特别是如果您的错误条件是错误条件或最常见的条件,则可以使用执行代码块的“除非”结构除非条件为真(即 if 的反面):

                unless ($foo) {
                    $bar;
                }
                

                【讨论】:

                  【解决方案16】:

                  首先,让我们先把最好避免使用“else”的情况放在一边(我希望每个人都同意这种情况确实存在,确定这种情况可能应该是一个单独的话题)。

                  所以,我们假设必须有一个“else”子句。

                  我认为可读性/可理解性至少强加了三个关键要求或规则,不幸的是,它们经常相互竞争:

                  1. 第一个块(“if”块)越短,就越容易掌握整个“if-else”结构。当“if”块足够长时,很容易忽略“else”块的存在。

                  2. 当“if”和“else”路径在逻辑上不对称时(例如“正常处理”与“错误处理”),在独立“if-else”构造中它并不是真的重要的是哪条路径是第一,哪条是第二。但是,当有多个“if-else”构造彼此靠近(包括嵌套),并且所有这些“if-else”构造具有相同类型的不对称性时 - 就是这样一致地安排这些不对称路径非常重要。

                    同样,它可以是“如果 ... 正常路径 ... else ... 异常路径”,也可以是“如果 ... 异常路径 ... else ... 正常路径”,但是它不应该是这两种变体的混合。

                    在所有其他条件相同的情况下,将正常路径放在首位对大多数人来说可能更自然(我认为这更多是关于心理学而不是美学:-)。

                  3. 以否定开头的表达式通常比不以否定开头的表达式可读性/可理解性差。

                  因此,我们有这三个相互竞争的要求/规则,真正的问题是:其中哪些比其他更重要。对于 Allen Holub 来说,第 1 条规则可能是最重要的一条。对于史蒂夫·麦康奈尔来说——这是规则#2。但我不认为你真的可以只选择这些规则中的一个作为单一的quideline。

                  我敢打赌,您已经猜到了我个人的优先事项(从我订购上述规则的方式来看 :-)。

                  我的理由很简单:

                  • 规则 #1 是无条件的,无法规避。如果其中一个块太长以至于它跑出屏幕 - 它必须成为“其他”块。 (不,仅仅为了减少“if”或“else”块中的行数而机械地创建函数/方法并不是一个好主意!我假设每个块已经有一个逻辑上合理的 最少行数。)

                  • 第 2 条规则涉及很多条件:多个“if-else”结构,都具有相同类型的不对称性,等等。所以它在很多情况下并不适用。

                    此外,我经常观察到以下有趣的现象:当规则#2 确实适用并且正确使用时,它实际上与规则#1 没有冲突!例如,每当我有一堆“正常与异常”不对称的“if-else”语句时,所有“异常”路径都比“正常”路径短(反之亦然)。我无法解释这种现象,但我认为这只是代码组织良好的标志。换句话说,每当我看到规则#1 和#2 发生冲突的情况时,我就会开始寻找“代码异味”,而且我经常会找到一些;重构之后 - tada!在规则 #1 和规则 #2 之间选择不再痛苦,:-)

                  • 最后,规则#3 的范围最小,因此最不重要。

                    此外,正如其他同事在此提到的,使用此规则“作弊”通常很容易(例如,写“if(disabled),,”而不是“if(!enabled)... ")。

                  我希望有人能理解这个作品......

                  【讨论】:

                    【解决方案17】:

                    作为一般规则,如果一个明显大于另一个,我将较大的一个设为if 块。

                    【讨论】:

                      【解决方案18】:
                      1. 把公共路径放在第一位
                      2. 将消极检查变为积极检查 (!full == empty)

                      【讨论】:

                      • 一个相关的问题是以否定的方式命名您的布尔变量。我讨厌这样:if (!noData) then
                      【解决方案19】:

                      我总是把最有可能的放在第一位。

                      在 Perl 中,我有一个额外的控制结构来帮助解决这个问题。 if的倒数。

                      unless (alive) {
                           go_to_heaven;
                      } else {
                           say "MEDIC";
                      }
                      

                      【讨论】:

                        【解决方案20】:

                        您应该始终将最可能的情况放在首位。除了更具可读性之外,它还更快。这也适用于 switch 语句。

                        【讨论】:

                          【解决方案21】:

                          当谈到我如何设置 if 语句时,我很害怕。基本上,我根据我正在寻找的内容进行设置,这导致一切都不同。

                          if (userinput = null){
                          爆炸暴力();
                          } 其他 {
                          实际做事;
                          }

                          或者类似

                          如果 (1+1=2) {
                          做事;
                          } 其他 {
                          猛烈爆炸();
                          }

                          if/else 语句的哪个部分实际上为我做事是我的一个坏习惯。

                          【讨论】:

                            【解决方案22】:

                            我一般把肯定的结果(所以方法)放在开头,所以:

                            if(condition)
                            {
                            doSomething();
                            }
                            else
                            {
                            System.out.println("condition not true")
                            }
                            

                            但如果要使用的方法条件必须为假,我会这样做:

                            if(!condition)
                            {
                            doSomething();
                            }
                            else
                            {
                            System.out.println("condition true");
                            }
                            

                            【讨论】:

                              【解决方案23】:

                              如果你必须有多个出口点,把它们放在最前面并说清楚:

                              if TerminatingCondition1 then
                                Exit
                              if TerminatingCondition2 then
                                Exit
                              

                              现在我们可以按照平常的方式进行:

                              if NormalThing then
                                DoNormalThing
                              else
                                DoAbnormalThing
                              

                              【讨论】:

                                猜你喜欢
                                • 2014-05-19
                                • 2010-10-06
                                • 1970-01-01
                                • 2013-02-13
                                • 2011-02-03
                                • 1970-01-01
                                • 1970-01-01
                                • 2019-06-10
                                • 2022-01-23
                                相关资源
                                最近更新 更多