为什么我们使用if,else-if而不是多个if块,如果主体是一个return语句



我一直习惯于使用if,else-if语句,而不是多个if语句。

示例:

int val = -1;
if (a == b1) {
   return c1;
} else if (a == b2) {
   return c2;
} ...
...
} else {
   return c11;
}

与示例2:相比如何

if (a == b1) {
   return c1;
}
if (a == b2) {
   return c2;
}
....
if (a == b11) {
   return c11;
}

我知道在功能方面它们是一样的。但是,如果还是如果,这是最好的做法吗?这是我的一个朋友提出的,当时我指出他可以用不同的方式构建代码库,使其更干净。这对我来说已经是一个长期的习惯了,但我从来没有问过为什么。

if-elseif-else语句一旦找到正确的语句,就会停止进行比较。if-if-if进行每一次比较。第一种更有效。

编辑:评论中指出,您在每个if块中执行一个return。在这些情况下,或者在控制将离开方法的情况下(例外),执行多个if语句和执行if-elseif-else语句没有区别。

然而,无论如何,最好使用if-elseif-else。假设您更改代码,使您不在每个if块中执行return。然后,为了保持效率,您还必须更改为if-elseif-else习惯用法。从一开始就将其设为if-elseif-else可以节省您将来的编辑,并且对阅读您的代码的人来说更清晰(通过浏览您的代码,可以看到我刚才对您的误解!)。

b1 == b2的情况如何?(如果是a == b1a == b2?)

一般来说,当这种情况发生时,以下两块代码很可能会有不同的行为:

if (a == b1) {
   /* do stuff here, and break out of the test */
} 
else if (a == b2) {
   /* this block is never reached */
} 

和:

if (a == b1) {
   /* do stuff here */
}
if (a == b2) {
   /* do this stuff, as well */
}

如果您想清楚地描述不同情况的功能,请使用if-elseswitch-case进行一个测试

如果您希望多个案例具有不同的功能,则使用多个if块作为单独的测试

这不是一个"最佳实践"的问题,而是定义你是有一个测试还是有多个测试。

在功能上不等效。

它在功能上等效的唯一方法是对a的每一个可能值执行"if"语句(即:每个可能的int值,如C中limites.h中所定义;使用int_MIN和int_MAX,或Java中的等效值)。

else语句允许您覆盖所有可能的剩余值,而不必编写数百万个"if"语句。

此外,使用if…else-if…else是更好的编码实践,就像在switch/case语句中,如果你不提供"默认"case语句,编译器会向你发出警告一样。这样可以防止您忽略程序中的无效值。例如:

double square_root(double x) {
    if(x > 0.0f) {
        return sqrt(x);
    } else if(x == 0.0f) {
        return x;
    } else {
        printf("INVALID VALUE: x must be greater than zero");
        return 0.0f;
    }
}

在这种情况下,是否要为x的每个可能值键入数百万if语句?对此表示怀疑:)

干杯!

这完全取决于您正在测试的条件。在你的例子中,它最终不会有什么不同,但作为最佳实践,如果你想最终执行其中一个条件,那么你最好使用if else

if (x > 1) {
    System.out.println("Hello!");
}else if (x < 1) {
    System.out.println("Bye!");
}

还要注意,如果第一个条件为真,则第二个条件根本不会被检查,但如果您使用

if (x > 1) {
    System.out.println("Hello!");
}
if (x < 1) {
    System.out.println("Bye!");
}

即使第一个条件为TRUE,也将检查第二个条件。优化器最终可能会解决这个问题,但据我所知,它的行为是这样的。另外,第一个是这样写和表现的,所以它总是我的最佳选择,除非逻辑另有要求。

ifelse if不同于两个连续的if语句。在第一种情况下,当CPU执行第一个if分支时,不会检查else if。在连续的两个if语句中,即使第一个if被检查并获取,如果条件为true,则下一个if也将被检查并获得。

我倾向于认为在代码更改时使用else if更容易、更健壮。如果有人调整函数的控制流,并将返回替换为副作用,或者将函数调用替换为try-catch,那么如果所有条件都是真正排他性的,则else-if将很难失败。这在很大程度上取决于你正在使用的确切代码来做出一般判断,你需要简洁地考虑可能的权衡。

在每个if分支中使用return语句

在代码中,每个if条件中都有return语句。当你遇到这样的情况时,有两种方法可以写出来。第一个是如何在示例1:中编写它

if (a == b1) {
   return c1;
} else if (a == b2) {
   return c2;
} else {
   return c11;
}

另一个如下:

if (a == b1) {
   return c1;
}
if (a == b2) {
   return c2;
}
return c11; // no if or else around this return statement

这两种编写代码的方法是相同的。

您在示例2中编写代码的方式不会在C++或Java中编译(在C中是未定义的行为),因为编译器不知道您已经涵盖了a的所有可能值,所以它认为函数中有一条代码路径,可以让您在不返回返回值的情况下到达函数的末尾。

if (a == b1) {
   return c1;
}
if (a == b2) {
   return c2;
}
...
if (a == b11) {
   return c11;
}
// what if you set a to some value c12?

在每个if分支中没有return语句

如果在每个if分支中没有return语句,则只有当以下语句为真时,代码的功能才会完全相同:

  1. 在任何if分支中,a的值都不会发生变化
  2. ==是一个等价关系(在数学意义上),并且b1b11都不在同一等价类中
  3. ==没有任何副作用

为了进一步澄清第2点(以及第3点):

  • ==在C或Java中始终是一个等价关系,并且从不产生副作用
  • 在允许重写==运算符的语言中,如C++、Ruby或Scala,重写的==运算符可能不是等价关系,并且可能会产生副作用。我们当然希望重写==运算符的人足够理智,能够写出一个没有副作用的等价关系,但这并不能保证
  • 在JavaScript和某些其他具有松散类型转换规则的编程语言中,存在==不可传递或不对称的情况。(在Javascript中,===是一个等价关系。)

在性能方面,示例#1保证不会在匹配的比较之后执行任何比较。编译器可能会优化#2以跳过额外的比较,但这不太可能。在下面的例子中,它可能不能,如果字符串很长,那么额外的比较就不便宜了。

if (strcmp(str, "b1") == 0) {
  ...
}
if (strcmp(str, "b2") == 0) {
  ...
}
if (strcmp(str, "b3") == 0) {
  ...
}

我更喜欢if/else结构,因为与开关一起评估每个变体中问题的所有可能状态要容易得多。我发现它更健壮,调试速度更快,尤其是当你在PHP等弱类型环境中进行多个布尔求值时,例如为什么elseif不好(为了演示而夸大):

if(a && (c == d))
{
} elseif ( b && (!d || a))
{
} elseif ( d == a && ( b^2 > c))
{
} else {
}

这个问题超过了4^2=16个布尔状态,这只是为了证明使情况变得更糟的弱类型效应。不难想象一个三态变量、三变量的问题以if ab elseif bc类型的方式涉及。

将优化留给编译器。

p>在大多数情况下,使用if-elseif-else和切换if-if-if语句更有效(因为它使编译器更容易创建跳转/查找表),并且更好的实践是因为它使代码更可读,而且编译器确保在切换中包含默认情况。这个答案,以及这个比较三种不同陈述的表格,是使用本页上的其他回答帖子以及类似SO问题的帖子合成的。

我认为这些代码片段是等效的,原因很简单,因为您有许多return语句。如果您只有一个return语句,那么您将使用这里不必要的else构造。

您的比较依赖于if语句体从方法返回控制这一事实。否则,功能将有所不同。

在这种情况下,它们执行相同的功能。在我看来,后者更容易阅读和理解,是我的选择。

它们可能会做不同的事情。

如果a等于b1b2,则输入两个if块。在第一个示例中,您只输入一个。我认为第一个例子更快,因为编译器可能必须按顺序检查每个条件,因为某些比较规则可能适用于对象。它可能能够优化它们。。。但如果你只想输入一个,第一种方法更明显,不太可能导致开发人员错误或代码效率低下,所以我绝对建议你这样做。

CanSpice的答案是正确的。性能的另一个考虑因素是找出哪个条件最常出现。例如,如果a==b1只出现在1%的时间内,那么您可以通过首先检查另一种情况来获得更好的性能。

Gir Loves Tacos的答案也不错。最佳做法是确保您涵盖了所有案例。

最新更新