70

这种方法:

boolean containsSmiley(String s) {
    if (s == null) {
        return false;
    }
    else {
        return s.contains(":)");
    }
}

等价地可以写成:

boolean containsSmiley(String s) {
    if (s == null) {
        return false;
    }

    return s.contains(":)");
}

以我的经验,第二种形式更常见,尤其是在更复杂的方法中(可能有几个这样的退出点),“抛出”和“返回”也是如此。然而,第一种形式可以说使代码的条件结构更加明确。有什么理由更喜欢其中一个吗?

(相关:一个函数应该只有一个 return 语句吗?

4

23 回答 23

93

else这种情况下,这将是多余的,并且会为函数的主代码创建不必要的额外缩进。

于 2010-07-16T04:08:02.660 回答
74

根据我的经验,这取决于代码。如果我“防备”某事,我会这样做:

if (inputVar.isBad()) {
    return;
}

doThings();

重点很清楚:如果该陈述为假,我不希望该功能继续。

另一方面,有些函数有多个选项,在这种情况下,我会这样写:

if (inputVar == thingOne) {
    doFirstThing();
} else if (inputVar == secondThing) {
    doSecondThing();
} else {
    doThirdThing();
}

即使它可以写成:

if (inputVar == thingOne) {
    doFirstThing();
    return;
}
if (inputVar == thingTwo) {
    doSecondThing();
    return;
}
doThingThree();
return;

它真的归结为最清楚地显示代码在做什么的方式(不一定是哪一段代码最短或缩进最少)。

于 2010-07-16T04:20:45.263 回答
37

这是一种称为Guard Clause的模式。这个想法是预先进行所有检查以减少嵌套条件以提高可读性。

从链接:

double getPayAmount() {
    double result;
    if (_isDead) {
        result = deadAmount();
    } else {
        if (_isSeparated) {
            result = separatedAmount();
        } else {
            if (_isRetired) {
                result = retiredAmount();
            } else {
                result = normalPayAmount();
            }
        }
    }

    return result;
}

使用 Guard Clause,您将看到以下结果:

double getPayAmount() {
    if (_isDead) return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired) return retiredAmount();

    return normalPayAmount();
};
于 2010-07-16T04:19:21.227 回答
8

你会看到这一切:

if (condition) {
    return var;
}
// by nature, when execution reaches this point, condition can only be false,
// therefore, the else is unnecessary
return other_var;

大多数情况下,添加 else 子句不仅在这种情况下是不必要的,而且很多时候,它会被编译器优化掉。

想一想计算机是如何看待这段代码的(就机器代码而言,这里为了演示目的简化为伪代码):

0x00: test [condition]
0x01: if result of test was not true, goto [0x04]
0x02: push [var] onto stack
0x03: goto [0x05]
0x04: push [other_var] onto stack
0x05: return from subroutine

代码(同样,这是一个伪代码而不是汇编)对于条件的行为方式完全相同if/then/else

许多人认为,一个函数有多个可能的退出点是不好的和/或令人困惑的做法,因为程序员必须考虑其代码的每条可能路径。另一种做法如下:

return (condition) ? var : other_var;

这简化了代码,并且不会创建任何新的退出点。

于 2010-07-16T04:24:44.283 回答
7

我更喜欢这样写:

boolean containsSmiley(String s) {
    return s != null && s.contains(":)");
}
于 2010-07-16T04:15:13.890 回答
4

else是多余的。此外,一些 IDE(Eclipse)和分析工具(可能是 FindBugs)可能会将其标记为警告或错误,因此在这种情况下,程序员可能会删除它。

于 2010-07-16T04:15:32.980 回答
4

就像任何关于编码风格的“讨论”一样,没有正确的答案。我更喜欢应用这些考虑:

  1. 代码是否在所有情况下都按预期工作。(最小意外原则)

  2. 下一个开发人员(我自己或其他人)能否理解它在做什么以及为什么。

  3. 代码在变化方面有多脆弱。

  4. 是简单的,因为它需要,没有更多。即没有工程过度或工程不足。

一旦我很高兴我满足了上述条件,其余的通常就会符合要求。

于 2010-07-16T06:15:33.647 回答
4

我更喜欢第一个选项,因为它更易于阅读。

作为一个类比,比较接下来的 2 个句子:“如果今天下雨,那就带伞,否则带太阳镜。” “如果今天下雨,那就带伞,带墨镜”。第一个句子对应于问题的第一个代码块,第二个 - 到第二个。第一个更清晰易读,不是吗?

于 2018-06-17T14:52:47.297 回答
3

其他人可能已经注意到这一点,但我建议不要在通常需要字符串的地方使用空值。如果您真的想要检查以防止有人传递空值,您可以使用断言(在开发时)或单元测试(部署):

boolean containsSmiley(String s) {
    assert s != null : "Quit passing null values, you moron.";
    return s.contains(":)");
}

我已经改用一般的经验法则:从不。曾经。传递空值,除非外部 API 调用明确要求它。第二:如果外部方法可能返回 null 值,请将其替换为合理的非 null 值(例如空字符串)或添加整洁的检查。我厌倦了重复if (thing == null)检查。

但这有点跑题了。我喜欢在 top 和 guard 子句上放置简短的条件,如果程序流程要求它永远不会到达那里,则删除 elses。

于 2010-07-16T11:09:27.700 回答
2

这是宗教争论,归根结底,这并不重要。我什至认为第一种形式在某些情况下更具可读性。如果您在 中有大量代码if-elseif-elseif-else,则乍一看会更容易看到默认返回是什么。

if (s == null) {
    return false;
}
else if (s.Contains(":))")) {
    return true;
}
else if (s.Contains(":-(")) {
    return false;
}

return s.contains(":)");
于 2010-07-16T04:18:31.650 回答
2

奥卡姆剃刀原则是“实体不得无必要地增加”。

于 2010-07-16T04:19:33.737 回答
2

if声明正在检查/执行您的合同/预期不接收空值。出于这个原因,我希望将它与函数的其余部分分开,因为它与您要完成的实际逻辑没有任何关系(尽管这种情况非常简单)。

不过,在大多数情况下,我更喜欢代码的意图尽可能明确。如果您可以对函数进行重构以使其对其他人更具可读性,请执行此操作。作为一名专业的程序员,你的目标应该是为那些在你之后维护你的代码的人(包括你自己 2 年后......)进行编程。你能做的任何事情来帮助他们都是值得的。

于 2010-07-16T04:22:17.707 回答
2

因为更好看。你知道你也可以使用 '{' '}' 来创建多层嵌套,但没有人真的只是为了它而这样做。

于 2010-07-16T08:26:56.250 回答
1

虽然 else 是正确的,并且在逻辑和可运行性方面没有任何问题,但我喜欢避免最初的 WTF 时刻,即函数在 if/else 范围之外没有返回语句。

于 2010-07-16T04:17:58.977 回答
0

第一种形式不那么冗长——当你返回一个值时,你会自动离开你所在函数的范围并返回给调用者,所以此后的任何代码都只会在 IF 语句不计算为 true 时执行然后返回任何东西。

于 2010-07-16T04:09:30.593 回答
0

我会争论可读性。如果您正在扫描代码屏幕以试图弄清楚代码的作用,这对开发人员来说是一个视觉提示。

...但这并不是真正需要的,因为我们都很好地评论了我们的代码,对吧?:)

于 2010-07-16T04:09:46.053 回答
0

在我看来,第二个更有意义。它更像是一个“默认”操作,就像一个开关。如果它不匹配任何其他出口点,那么就这样做。你真的不需要 else 那里。我会说如果整个函数只有 if 和 elseif,那么 else 在那里是有意义的,因为它是一个巨大的条件。如果其中运行了多个条件和其他函数,则将使用最后的默认返回。

于 2010-07-16T04:10:55.150 回答
0

如您所见,不同的人对可读性有不同的看法。有些人认为更少的代码行往往会使代码更具可读性。其他人认为第二种形式的对称性使其更具可读性。

我的看法是,这两种观点可能都是正确的……对于持有它们的人来说。推论是,您无法编写每个人都认为可读性最佳的代码。因此,最好的建议是遵循您的强制性编码标准所说的(如果它对此有任何说明)并通常使用您的常识。(如果你背负着一些吵吵嚷嚷的傻瓜,他们坚持认为他的方式是“正确的”……就顺其自然吧。)

于 2010-07-16T04:22:43.480 回答
0

因为如果在这种情况下使用 else ,则在 eclipse 中有一个可选的(默认关闭)警告;)。

于 2010-07-16T05:00:04.663 回答
0

好吧,其中一些原因只是惯例,但上面的表格有一个优点......

在编写 return 语句时,通常会将最后一个语句设置为默认返回值。这主要有助于重构 - else 子句往往会被包裹在其他结构中,或者可能会意外地移入树的更深处。

于 2010-07-16T05:37:59.810 回答
0

从维护的角度来看,我更喜欢一个退出点而不是多个退出点。最终结果可以在一个出口点而不是 n 个出口点进行修改(或修饰)。

于 2010-07-16T05:46:12.827 回答
0

第二种形式如果更简单/更短。这并不总是意味着更清晰。我建议你做你认为最清楚的事情。我个人会写。

static boolean containsSmiley(String s) { 
    return s != null && s.contains(":)"); 
} 
于 2010-07-16T06:03:47.437 回答
0

因为它与编写以下内容之一相同,它带来了有关程序员意图的证据:

boolean containsSmiley(String s) {
    if (s == null)   // The curly braces are not even necessary as the if contains only one instruction.
        return false;

    return s.contains(":)");
}

甚至这样:

boolean constainsSMiley(String s) {
    return string.IsNullOrEmpty(s) ? false : s.Contains(":)");
}

这两种形式是:

  1. 更优雅;
  2. 更容易阅读;
  3. 对于阅读程序员来说更精简、更快捷。
于 2010-07-16T08:37:13.477 回答