【问题标题】:When to use 'if…else if' and when to use何时使用“if…else if”以及何时使用
【发布时间】:2010-09-02 19:25:27
【问题描述】:

1) 我知道if…else if 语句是如何工作的,但是在下一个示例中,就结果值而言,这两种方法都是相同的。那么我使用这两种方法中的哪一种,或者我应该总是选择一种在语义上最接近代码试图做的事情是否重要(这里我猜这两种方法在语义上是完全不同的)?那么你会使用哪种方法,为什么?

protected string GetNumberDescription(int value)
{
    if (value >= veryBigNumber)
        return veryBigNumberDescription;
    else if (value >= bigNumber)
        return bigNumberDescription;
    else if (value >= smallNumber)
        return smallNumberDescription;
    else
        return "";
}

protected string GetNumberDescription(int value)
{
    if (value >= veryBigNumber)
        return veryBigNumberDescription;
    if (value >= bigNumber)
        return bigNumberDescription;
    if (value >= smallNumber)
        return smallNumberDescription;
    else
        return "";
}

2) 我注意到在编写if ... else if 语句时丢失的代码使用以下格式:

if ...
else if ...
else ...

但不是(至少在概念上)更正确的方式:

if ...
else
  if ...
  else ...

【问题讨论】:

  • Java 上下文中的类似问题:stackoverflow.com/questions/3579918/…
  • 当有人在 ifs 块处理的某处插入 else if 语句时,执行第一个可能会导致问题。问题是代码冗余和性能问题
  • 请记住,if/else,像 switch() 经常表示糟糕的 OO。在您的情况下,每次有人添加新级别的数字时,您都会强制编辑您的 if 块 - 将它们放入集合中并对其进行迭代。

标签: c# coding-style


【解决方案1】:
  1. 您可能应该使用第一个。它更容易被接受,也更合乎逻辑。 (如果这个条件为真,那么之后你什么都不关心......在你的代码中指出这一点。)
  2. 使用第一种方式(else if)通常更容易被接受,而且更简洁易读。 Python 甚至有一个特定的关键字 (elif)。

【讨论】:

  • 第一个不会保存不必要的评估,因为每个真实条件都以 return 语句结束。
  • @Michael La Voie - 是的,对不起,我刚刚意识到我的错误并纠正了它......我没有立即注意到回报
  • 同意,第二种风格只是糟糕的编码。
  • @froadie:为什么使用 else if 会“更紧凑”?它不那么紧凑,因为您必须输入更多内容。可读性也值得商榷。不过,我确实同意第一种风格可能更常见。
  • @gmagana:为什么你认为它是“糟糕的编码”?在我看来,它们在功能上是等效的并且同样具有可读性。说一个“坏”而不说原因似乎是一种非常主观的评估。
【解决方案2】:

我个人会使用

protected string GetNumberDescription(int value)
   {
       if (value >= veryBigNumber)
           return veryBigNumberDescription;
       if (value >= bigNumber)
           return bigNumberDescription;
       if (value >= smallNumber)
           return smallNumberDescription;
       return string.Empty;
   }

这完全取决于你的函数在做什么。如果它像这样简单,那就保持简单。如果您可能需要在返回结果之前对结果进行一些处理,那么我会使用 Egrunin 的解决方案

【讨论】:

  • for(Number number : numberCollection) if(value >= number.value) return number.description; throw new IllegalArgumentException();----这显然应该是 NumberCollection 类中存在的一个方法,它变成 numberCollection.getDescription(value);不要害怕编码好。 (我并不是暗示答案对于教学目的来说不是更清晰/更好,只是采取下一步)数字也是其中一个词,如果你连续输入太多次它看起来不正确。
【解决方案3】:
protected string GetNumberDescription(int value) 
{ 
    string ret = "";

    if (value >= veryBigNumber) 
        ret = veryBigNumberDescription; 
    else if (value >= bigNumber) 
        ret = bigNumberDescription; 
    else if (value >= smallNumber) 
        ret =  smallNumberDescription; 

    return ret; 
} 

问题已经回答了,但我添加了这个是因为:

  1. 从函数中单独退出是有好处的。
  2. 重复测试同一个变量,所以 if/elseif 是最合适的。

编辑添加:

我通常非常喜欢在这种情况下尽早退出函数:

if (conditionOne)
{
    if (conditionTwo)
    {
        if (conditionThree)
        {
            interesting_stuff();
        }
    }
    else
        boring();
}
return;

我很高兴看到这个:

if (!conditionOne)
    return;

if (!conditionTwo)
{
    boring();
    return;
}

if (!conditionThree)
    return;

interesting_stuff();
return;

但是当在许多不同的嵌套级别存在退出时,在阅读代码时很容易错过其中一个。

另外,正如 cmets 中提到的:单个出口意味着只有一个地方可以放置断点。

【讨论】:

  • 你能扩展你的第一点吗?我通常更喜欢其他方式,以使我的代码更小。
  • 因为return充当控制流语句,使得阅读代码更加复杂。如果您在整个函数体中都散布了 return 语句,那么当您尝试了解函数的工作原理时,您就有更多的使用场景需要考虑。如果您尝试修改函数并向其添加更多代码,那么如果您总是希望它们执行,则仅限于将语句放在方法的开头,因为如果将它们放在末尾,那么在某些情况下,语句会胜出不要那么远。但也许你需要分析结果,所以不能放在开头。
  • +1 我们可以在单次返回上放一个断点来查看结果。
【解决方案4】:

如果条件是互斥的,那么我认为 else if 模式是最好的。随着代码的发展和变得更加复杂,意外中断将变得更加困难,因为您的控制流更准确地表示了逻辑的互斥性。

考虑是否有人将您的第一个示例重构为类似于 egrunin 的示例,这样只有一个返回,但考虑到他们忘记在每个返回中添加“else”:

protected string GetNumberDescription(int value) 
{ 
    string ret = "";

    if (value >= veryBigNumber) 
        ret = veryBigNumberDescription; 
    if (value >= bigNumber) 
        ret = bigNumberDescription; 
    if (value >= smallNumber) 
        ret =  smallNumberDescription; 
    // BUG: very big numbers still fall through to this last condition, 
    //         because they meet all conditions

    return ret; 
} 

他们刚刚引入了一个错误。他们可能没有意识到回报是所表达逻辑的一部分。虽然这里的错误很明显,但在现实生活中,几年后这段代码可能会变得更复杂、更长、更难阅读,因此更容易犯这样的错误。

检测意外错误

此外,如果必须满足至少一个条件(即永远不应返回空字符串),那么如果您假设所有情况都应处理,我会在最后的 else 中抛出异常。这将检测代码中的错误,其中代码通过所有其他错误并且没有找到匹配条件。如果您不抛出异常,则该错误可能会导致奇怪的行为。其余代码将如何处理未初始化的空字符串?很大程度上取决于手头的代码。抛出异常的想法是在错误所在的位置引发硬故障,以便可以轻松识别和纠正它,而不是让应用程序继续运行并返回无效的返回。

if (value >= smallNumber) 
    ret =  smallNumberDescription; 
else if(...)
    ret =  otherNumberDescription; 
else
    throw new InvalidOperationException($"No condition matched for value={value}");

return ret; 

如果与适当的异常日志记录/通知配合使用,那么这将使您能够意识到这个错误并添加一个条件来处理意外值。对于非常简单的情况并不是真正必要的,但对于主动查找和纠正此类代码中的错误非常有帮助。

【讨论】:

    【解决方案5】:

    它们在语义上是相同的。我怀疑性能上有什么不同,所以选择是关于风格和可读性。好奇的话,把上面的代码编译成程序集,用Reflector看看IL有没有什么不同。我的猜测是,如果有的话,差别不大。

    【讨论】:

    • 我认为发布者意识到生成的代码将是等效的。他在问是否有理由优先选择一种编码风格
    • @Karmastan:OP 声明“在语义上它们完全不同”。它们在语义上没有区别。我对风格上的差异没有强烈的看法,只是指出它们的意思是一样的。
    【解决方案6】:

    这主要是一个偏好问题。当使用 return 语句时,我认为你不需要 elses,因为很明显函数就在那里结束。这完全取决于情况和风格,因此没有明确的“最佳”方法。

    【讨论】:

    • +1 表示这取决于情况/风格——这是人们争论的事情,当任何一个都可以接受时。
    • 我在某种程度上同意。在这种情况下,显然条件是相互关联的,else-if 风格更能防御错误,所以我倾向于说这不是风格问题。
    【解决方案7】:

    我建议前者,因为您在四种可能性之间做出合乎逻辑的选择。您正在将决策分组到一个 if/else if 块中。

    如果您像在第二个函数中那样将决策分开,它们似乎根本没有关联。显然它们是,因为 value 变量是重复的,但它可能不一定很明显。

    此外,当您没有要返回的内容时,请返回 null,而不是空字符串。在我维护的一个应用程序中,仍然需要使用 if (value == null || value != "")。

    【讨论】:

    • string.IsNullOrEmpty(value) :-)
    • 天哪,我不敢相信我从未想过这个函数会成为标准库的一部分。谢谢!
    【解决方案8】:

    您想使用“else if”,因为只有在第一个“if”未被接受时才会触发。在你的情况下,3个if语句是可以的,仅仅是因为如果后面的“if”是正确的,那么前面的“if”就不可能了。

    如果你的所有 if 语句都正确,你会想使用 else if。

    例如:

    if (8<10) {
    return 10;
    }
    if (8<9) {
    return 9;
    }
    

    这是一个非常简单的例子,但你明白了。当您只想触发一个“if”语句时,可以使用“else if”。

    【讨论】:

      【解决方案9】:

      我会使用第一个选项,因为它在代码中显示比较是“分组”的。对于 2,我认为它们最终会使用完全相同的 IL 代码。

      我始终确保最常见的情景位于 if..else 语句的首位,因为这样最有利于性能。

      【讨论】:

        【解决方案10】:

        替代方案在语义上非常接近,所以没那么重要。正如一些答案中所建议的,将结果放入字符串变量中并在最后返回可能更好地代表您在方法中所做的事情。

        另一种选择是使用条件操作数,它可以为您提供更少冗长的代码:

        protected string GetNumberDescription(int value) {
          return
            value >= veryBigNumber ? veryBigNumberDescription :
            value >= bigNumber ? bigNumberDescription :
            value >= smallNumber ? smallNumberDescription :
            String.Empty;
        }
        

        【讨论】:

        • 我觉得这个比较难理解。
        • 我必须思考才能理解这段代码。也许我不习惯看到这种风格。用 if...else if...else 我马上就明白了,不用多想。我使用并看到这样的代码 a>b ? c : d 我觉得还可以。在嵌套条件操作数时,我认为更难弄清楚发生了什么。
        【解决方案11】:

        使用return 语句,这真的没关系。不过,我完全不同意那些说第二种情况更糟的人。是什么让情况变得更糟?

        回答你的第二个问题:你说的很对。其实不仅if/else if的两种格式化方式有效是一样的;它们实际上是字面意思相同的。 C# 不强制执行特定的格式化样式,因此它们都是相同的:

        // This...
        if (A())
            return "Congrats!";
        else if (B())
            return "Not bad!";
        else if (C())
            return "Next time...";
        else
            return "Uh-oh.";
        
        // ...is the same as this...
        if (A())
            return "Congrats!";
        else
            if (B())
                return "Not bad!";
            else
                if (C())
                    return "Next time...";
                else
                    return "Uh-oh.";
        
        // ...which is the same as this...
        if (A()) return "Congrats!"; else
                               if               (B())
                     return "Not bad!";
        
        
        else
        { if
        (C())
        
        
             return
        
        
        "Next time...";else{
        return "Oh-oh.";
        }}
        

        【讨论】:

          【解决方案12】:
          protected string GetNumberDescription(int value) 
          { 
              return new NumberDescriptor(value).toString();
          } 
          

          你所需要的只是隐藏你的逻辑。 不要太认真,但是……我会那样做。我知道,这里没有回答您的问题,但是本主题中已经有足够的 if/then/else 示例。

          【讨论】:

            【解决方案13】:

            1) 第一个更为普遍且普遍。因此,坚持标准做法是明智之举。

            2) 通常当我们有多个条件语句时,else if 用于中间条件输出,else 用于最后一个条件输出。此外,它可能更有效,因为我相信它不必每次都检查相同的条件,这与 if-else-if-else... 类型不同。

            它更像是为您提供的一种选择,您可以在通往同一目的地的众多路径中找到自己的选择。但是您经常听到或看到的选择是由于许多用户和专家的经验而流行的选择。

            【讨论】:

              【解决方案14】:

              如果函数被认为返回四种可能性之一,我会说使用第一种,所有这些都被认为是“成功的”。如果某些返回表示“失败”,我将使用后一种样式。例如(玩具示例——对于错误情况,投掷可能会更好):

              字符串 get_suit_name(int suit) { string suit_names[4] = {"clubs", "diamonds", "hearts", "spades"}; if (0 > suit) /* 避免 LT 符号 */ return "花色是不恰当的负数!"; 如果 (西装 >= 4) return "西装太大了!"; 返回西装名称[西装]; }

              【讨论】:

                【解决方案15】:

                在我看来,这些优先级从高到低依次排列。

                功能性/实现最终目标 降低任何范围/级别的成本 可读性

                ^^^^ 请注意,许多人会在我的列表中对此提出异议,并将可读性称为降低成本,这实际上取决于您的工作环境。和最终目标,但我发现这是最常见的。但猜测来自一个没有多大意义的菜鸟吧:D

                在那之后你几乎是花生。也一样吗? : 操作员。如果你确信它更具可读性,那就去吧。功能方面,这并不重要。但是 if(){}else{} 与 if(){}else if(){} 有很大不同所以选择什么是最佳代码(最高想要的结果减去意外结果 - 0.5* 可读性分数)几乎就是我想要的在。

                现在看到代码对您来说也是如此,一个新的属性开始发挥作用,这个代码会在以后的某个阶段被编辑吗?如果是这样,那么最好的选择是什么?不要忘记代码的未来,也不要忘记 cmets,这是一个免费的奖励。如果您暂时无法弄清楚,您甚至可以同时进行这两种操作,然后只评论另一个,但这确实会留下草率的文字墙,以便稍后在清理/发布时删除。

                【讨论】:

                  【解决方案16】:

                  对于提供的示例,如果您愿意牺牲大量可读性和可能的​​一点速度,则无需担心“if”语句:

                  string [] descriptions = {"", smallNumberDescription, bigNumberDescription, veryBigNumberDescription};
                  
                  protected string GetNumberDescription(int value)
                  {
                      int index = Convert.ToInt32(value >= smallNumber) + Convert.ToInt32(value >= bigNumber)
                                  + Convert.ToInt32(value >= veryBigNumber);
                      return descriptions[index];
                  }
                  

                  【讨论】:

                    【解决方案17】:

                    就性能而言,它们可能是相同的,具体取决于编译器/解释器。在我看来,第二个更好....更容易调试。

                    【讨论】:

                      【解决方案18】:

                      嗯,在你的特殊情况下,我建议这样做:

                          protected string GetNumberDescription(int value) {
                              if (value >= veryBigNumber)
                                  return veryBigNumberDescription;
                              if (value >= bigNumber)
                                  return bigNumberDescription;
                              if (value >= smallNumber)
                                  return smallNumberDescription;
                      
                              return "";
                          }
                      

                      由于您在任何真实条件下从函数返回,因此无需使用“else if”。在您的情况下,如果任何条件为真,则代码将不会进一步执行。 此外,最后一个“else”不是必需的,因为只有当上述所有条件都为假时,控件才会到达该点。

                      这完全取决于您在哪里使用它以及如何使用它。主要动机是最大限度地提高代码的可读性,并使其尽可能优雅和易于理解。

                      许多人更喜欢第一种方式,因为它在您的代码中表明您不在乎一旦满足条件就会发生什么。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2014-04-14
                        • 1970-01-01
                        • 1970-01-01
                        • 2020-11-06
                        • 1970-01-01
                        • 1970-01-01
                        • 2019-04-25
                        • 2012-11-02
                        相关资源
                        最近更新 更多