编程偏好 - 在多个返回语句中使用else if语句?

8
代码如下:
public String getTemperatureMessage(double temp)
{
    if(temp < 32)
        return "Freezing";
    else if(temp < 60)
        return "Brr";
    else if(temp < 80)
        return "Comfortable";
    else
        return "Too hot";
}

关于上面的代码片段,else if 从技术上讲是多余的,不会改变行为。然而,我倾向于将它们放在那里以强调条件是互斥的。你有什么想法?是不必要的还是更清晰?


这同样适用于大多数其他编程语言。我会添加“与语言无关”的标签。 - Dima
2
编辑战 :) 我相信有一个错误会阻止代码在帖子开头显示为代码... - viraptor
4
以下是需要翻译的内容:Duplicate: https://dev59.com/dk3Sa4cB1Zd3GeqPt0z3, https://dev59.com/gXA75IYBdhLWcg3wf5NV, https://dev59.com/PXVD5IYBdhLWcg3wQZUg, http://stackoverflow.com/questions/3533779/should-else-be-kept-or-dropped-in-cases-where-its-not-needed, https://dev59.com/w3A65IYBdhLWcg3w5y_B这些链接是与编写代码时关于if-else语句中是否需要明确声明else,函数中是否应只有一个返回语句,以及在if-else语句中是否需要保留不必要的else等问题相关的讨论。 - gnovice
1
这个问题更多是关于人们的意见,应该关闭。 - user1043000
16个回答

10

在这种特定情况下,唯一可行的替代方案是使用条件运算符?:

public String getTemperatureMessage(double temp) {
    return temp < 32 ? "Freezing"
         : temp < 60 ? "Brr"
         : temp < 80 ? "Comfortable"
         : "Too hot";
}

这里有一个问题,如何让初学者更容易理解。

参考资料

相关问题


5
那是我见过的最丑的三元句子。 - TheLQ
顺便提一下,这里每天的温度在85~90华氏度之间参考,但我个人认为很舒适 :) - BalusC
@BalusC 我使用摄氏度。@Quackstar 我不同意,更丑陋的是所有内容都在一行上。我认为这里的结构方式增加了清晰度。 - Stephen Denne
我同意这是最好的方法;它能够很好地工作,因为它是避免早期返回和可变变量的唯一选择,并且使得没有副作用非常明显。 - Dax Fohl
我原则上不赞成嵌套三元运算符,但我认为这看起来非常好! - Charles Wood
显示剩余2条评论

9

这取决于很多因素,比如你的代码有多复杂。对于像这样简单的示例,我会将返回值放在if语句的同一行,而不使用else。结构和行为是清晰的:

public String getTemperatureMessage(double temp)
{
    if(temp < 32) return "Freezing";
    if(temp < 60) return "Brr";
    if(temp < 80) return "Comfortable";
    return "Too hot";
}

当我有更复杂的代码时,我发现不使用返回或continue / break中断嵌套,而是分配给状态或结果变量很有用。即使块只有一个语句,我也会包含{},主要是为了保持代码结构的一致性,但也稍微降低了后续编辑忘记将语句更改为块的风险。

如果这个例子更复杂,我可能会这样编写代码:

public String getTemperatureMessage(double temp) {
    String result;
    if(temp < 32) {
        result = "Freezing";
    } else {
        if(temp < 60) {
            result = "Brr";
        } else {
            if(temp < 80) {
                result = "Comfortable";
            } else {
                result = "Too hot";
            }
        }
    }
    return result;
}

8

如果一个函数有多个“成功”的返回值,我会使用if/else来选择它们之间的返回值。如果一个函数有一个正常的返回值,但是有一种或多种可能会异常退出的方式,我通常不会在正常路径上使用“else”。例如,我认为以下写法更加自然:

int do_something(int arg1)
{
  if (arg1 > MAX_ARG1_VALUE)
    return ARG1_ERROR;
  ... main guts of code here
  return 0;
}

比如说:
int do_something(int arg1)
{
  if (arg1 > MAX_ARG1_VALUE)
    return ARG1_ERROR;
  else
  {
    ... main guts of code here
    return 0;
  }
}

或者

int do_something(int arg1)
{
  if (arg1 <= MAX_ARG1_VALUE)
  {
    ... main guts of code here
    return 0;
  }
  else
    return ARG1_ERROR;

如果有多个事情可能“出错”,这种区分尤其重要,例如:

int do_something(int arg1)
{
  if (arg1 > MAX_ARG1_VALUE)
    return ARG1_ERROR;
  ... some code goes here
  if (something_went_wrong1)
    return SOMETHING1_ERROR;
  ... more code goes here
  if (something_went_wrong2)
    return SOMETHING2_ERROR;
  ... more code goes here
  if (something_went_wrong3)
    return SOMETHING3_ERROR;
  return 0;
}

在这种情况下,嵌套的if/else语句可能会变得很丑陋。使用此方法最重要的注意事项是,提前退出时的任何清理代码必须明确给出,否则必须使用包装函数来确保清理。

@John Kugelman: 谢谢。我刚才又想到另一个问题:如果函数的最后两行分别是条件返回和无条件返回,那就意味着这个函数可能还有其他出口。如果我的函数以两个条件返回(都是"成功"的)结尾,但也有其他失败的返回,我会添加注释,提示寻找其他出口,比如"如果我们到了这里......"。 - supercat

7
有人会认为这里的问题是多重返回,但这不是我的观点。
在我看来,if/else if非常重要,因为即使在你的情况下返回一些值,删除else也意味着你不会放置它们,如果没有返回语句,这将意味着完全不同的事情。
此外,想象一下,有一天有人想编辑你的代码,并清理一个单一的返回,这个人可能会误解你的代码,并犯下如下严重错误:
public String getTemperatureMessage(double temp){
    String message;
    if(temp < 32)
        message = "Freezing";
    if(temp < 60)
        message = "Brr";
    if(temp < 80)
        message = "Comfortable";
    else 
        message = "Too hot";
    return message;
}

为了阐明我的观点,保留else语句可以使你的代码更加清晰。

@Callum,我并没有说返回值是问题。请阅读我的帖子。 - Colin Hebert
@Colin:即便如此,只有一个出口点的情况会导致额外的代码混乱和复杂性。你可能会犯一个简单的错误,比如在方法末尾_忘记返回变量_。(哈哈,在我打字的时候你已经修复了它 :) ) - Callum Rogers
@Callum,还不是我想说的。 (而IDE比文本区域更适合编写代码)。 - Colin Hebert
1
@OMG Ponies:我坚决不同意(对于这样一个简单的方法来说),而且@Colin:我现在明白你的观点了,就问题而言,这是一个很好的答案(我被看起来像SEPSEP教条主义的东西吓到了),所以还给你2个声望。 - Callum Rogers
3
@Recurse,请阅读我的帖子和原帖!他问是否有用else语句,我回答说有,并举了一个例子说明为什么有用!(你一定是在开玩笑吧,难道真的有人读过这个问题吗?) - Colin Hebert
显示剩余6条评论

2

对于简单的一行代码,我倾向于省略else,但如果有更复杂的if块,我倾向于使用else以清晰地表明条件是相互排斥的。


1
public String getTemperatureMessage(double temp)
{
    String retval = null;
    if(temp < 32)
        retval = "Freezing";
    else if(temp < 60)
        retval = "Brr";
    else if(temp < 80)
        retval = "Comfortable";
    else
        retval = "Too hot";
    return retval;
}

你为什么要将 retval 赋值为 null? - ILMTitan
只是出于习惯,确保它始终被初始化为某些东西。 - Paul Tomblin
3
在Java中,“自动”(即不加思考)这样做是一个不好的想法。这会阻止Java编译器指出在代码路径中遗忘分配(非空)值的错误。 - Stephen C

1

对于一个简单的if语句,没有太多代码行并且有多个返回值是没有问题的。然而,没有什么比这更让我愤怒的了:

function doTemperatureCalculations(double temperature) {
  if (temperature < 20) {
    /* 
      Gazillion lines of code here .....
     */
    return "Very cold!";
  } else if (temperature < 40) {
    /*
      Another gazillion loc .....
     */
    return "Summer in the North Pole.";
  } else {
    /*
      Multiple returns embedded throughout ....
     */
  }
}

我同意,如果你要有多个返回值,那么让它们易于找到是很重要的。但问题是,当if块总是以return结尾时,是否值得使用else - Stephen Denne

1
在这种情况下更清晰。在一般情况下,您可能希望省略else语句,因为它们可能会导致更多的嵌套和代码复杂性。例如:

if (error condition) {  
  do some stuff;
  return;
} else {
  do stuff;
  if (other error condition) {
     do some stuff1;
     return;
  } else {
     do some other stuff;
     return

  }
}

以下代码将嵌套级别保持在较低水平,从而降低了代码复杂度:

if (error condition) {
  do some stuff;
  return;
}
do stuff;
if (other error condition) {
  do some stuff1;
  return;
}
do some other stuff;
return;

在你的例子中,无论哪种方式都很容易。但在许多情况下,最好使用查找表来处理这种情况,并从文件/数据库中读取值。在C语言中,为了效率,通常会将其编码为结构体数组。

else语句确实增加了一些清晰度,因为它明确了这些情况是互斥的。然而,像你这样返回的习惯用法对许多程序员来说是显而易见的,所以无论哪种方式,大多数人都知道你的意思。

我可以想到一个else语句的优点。如果您想添加一个新的最后一个情况,如果没有else语句,您可能会忘记在当前的“太热条件”中添加if语句,例如,如果您想在120时添加“濒死”等情况。而有了else语句,您知道需要将最终的else放在“濒死”之前,这样您更有可能考虑将else if放在“太热”的前面。此外,如果您只在“濒死”上放置else语句,您将收到编译错误,这迫使您思考。


1
个人认为else是不必要的。由于这个问题被标记为[language-agnostic],我将提供一些例子来说明我的写法:
def temperature_message(temp)
  return 'Freezing'    if temp < 32
  return 'Brr'         if temp < 60
  return 'Comfortable' if temp < 80
  'Too hot'
end

这是典型的守卫条款风格,我个人和整个 Ruby 社区都经常使用。

def temperature_message(temp)
  case
  when temp < 32
    'Freezing'
  when temp < 60
    'Brr'
  when temp < 80
    'Comfortable'
  else
    'Too hot'
  end
end

这是一个典型的switch,你可以在一些不太强大的语言中找到它。这可能是我不会使用的一种,我会像这样重构它:

def temperature_message(temp)
  case temp
  when (-1.0/0.0)...32
    'Freezing'
  when 32...60
    'Brr'
  when 60...80
    'Comfortable'
  else
    'Too hot'
  end
end

虽然我必须承认,我仍然觉得第一种最容易读懂。

既然这基本上是一个映射表,我会尝试格式化它,以便每个阅读代码的人都能立即看到“表”的特征:

def temperature_message(temp)
  case temp
  when (-1.0/0.0)...32 then 'Freezing'
  when         32...60 then 'Brr'
  when         60...80 then 'Comfortable'
                      else 'Too hot'
  end
end

这同样适用于您的原始Java实现:

public String getTemperatureMessage(double temp) {
    if(temp < 32) return "Freezing";
    if(temp < 60) return "Brr";
    if(temp < 80) return "Comfortable";
    else          return "Too hot";
}

当然,由于它基本上是一个映射表,你可以将其实现为一个映射。
def temperature_message(temp)
  {
    (-1.0/0.0)...32       => 'Freezing',
            32...60       => 'Brr',
            60...80       => 'Comfortable',
            80..(1.0/0.0) => 'Too hot'
  }.detect {|range, _| range.include?(temp) }.last
end

1

多余的else让我感到不舒服。额外的语法和缩进使我更难阅读。我刚刚从继承的代码中删除了一堆这样的代码。

大多数冗余代码都是错误的,因此即使您有意放置了冗余的“else”,在我看来也像是一个错误。我的印象是,该代码最初是没有嵌入返回的,然后有人重写它以具有嵌入式返回,但他们懒得删除else。

单个if / return很容易理解;连续4个也是如此。它是“该情况已完成;让我们继续”。长串的if / else可能很难阅读;您需要一直阅读到底部才能找出发生了什么。拯救的方法是它允许使用单个return--这是一个被高估的功能,但我承认它确实提供了一些价值。然而,长串的if / else与混合在else中的返回结合在一起是两全其美的最糟糕的情况--所有多个返回的缺点,并且看起来像一个大型构造体,您必须一次性掌握。呃。

从理论上考虑,这个区域在return和else之间本质上是无法到达的代码。当然,它只包含空格,但这个区域根本不应该存在。
最后,一个if/return/else的冗余示例。我最近看到了一些这样的代码。为什么世界上会有一个else块呢?else块中的代码在与其直接后面的代码相同的条件下执行:
...
if (temp < 35)
{
  foo.status = TOO_COLD;
  return;
}
else
{
  foo.status = TEMP_OKAY;
}
launch_the_rocket(now);
return;

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