C if语句中的或条件链

C if语句中的或条件链,c,if-statement,C,If Statement,在我的代码中有一个if语句,它看起来像: if(someFunction1(a) || someFunction2(b->b1,c) || *d == null || somefunction3(e) > f * g || !e->e1 || ...){ return 0; } else { do_something; } 在我的代码中,实数变量和函数名几乎是三行中的条件,看起来非常简单。所以我决定把它改写成: if(someFunction1(a)){ r

在我的代码中有一个if语句,它看起来像:

if(someFunction1(a) || someFunction2(b->b1,c) || *d == null || somefunction3(e) > f * g || !e->e1 || ...){
   return 0;
} else {
   do_something;
}
在我的代码中,实数变量和函数名几乎是三行中的条件,看起来非常简单。所以我决定把它改写成:

if(someFunction1(a)){
   return 0;
} else if(someFunction2(b->b1,c)){
   return 0;
} else if(*d == null){
   return 0;
} else if(somefunction3(e) > f * g){
   return 0;
} else if(!e->e1){
   return 0;
} else if(...){
   return 0;
} else{
   do_something;
}

是否有任何理由认为我不应该这样做?

我认为除了冗余之外,这段代码中没有其他问题。如果您必须对return语句进行更改,那么根据您的实现,您必须在6个位置进行更改

但在第一个实现中不会出现这种冗余


两者在其他方面是相似的。

正如您所问的,因为可读性,您可能希望将长条件重新排列为谓词变量,说明为什么必须返回零

bool unnecessary = someFunction1(a) || someFunction2(b->b1,c);
bool beyondTolerance = somefunction3(e) > f * g;
bool invalidInput = *d == nullptr || !e->e1;

if (unnecessary || beyondTolerance || invalidInput)
    return 0;
else 
    ...

这是Martin Fowler的重构。

您可以通过以下方式进行重构

int not_valid = someFunction1(a) || 
                someFunction2(b->b1,c) || 
                *d == null || 
                somefunction3(e) > f * g || 
                !e->e1 || ...;

if ( !not_valid )
{
   do_something;
}

return !not_valid;

您可以选择一个更合适的名称,而不是无效名称。:

从纯语义语法的角度来看,它们之间没有有效的区别。但是,如果您关心可读性,为什么不使用“DatenWalf”格式样式呢?我是在过去5个项目中开发这种样式的:

if( someFunction1(a)
 || someFunction2(b->b1,c)
 || *d == null
 || somefunction3(e) > f * g
 || !e->e1
 || ...
){
   return 0;
} else {
   do_something;
}

你看到所有的东西都排成一排了吗?它看起来真的像一根管子,程序在达到一个符合条件之前一直在往下掉。如果您有
&&
,它看起来像一个不能被打破的操作链。

选项1:

  • 简洁
  • 一个退出点,以避免返回语句的冗余
备选案文2:

  • 准确的故障点可以很容易地诊断,即可以将日志添加到每个分支以检测故障

首先,如果不提供一些基本原理,你就不能回答这个问题,否则答案将变得完全主观。我会警惕那些回答“这样做,因为我最喜欢这样做”的人,因为他们没有提供任何理由


看看代码,显然是在函数内部进行了大量的错误检查。在实际的代码示例中,所有此类错误处理通常需要大量注释来描述每个错误条件,因为具有大量错误处理的函数往往比较复杂

如果把代码写为一个语句不是一个好主意,因为如果你必须在语句中间插入注释,代码就会变得一团糟。 基于上述理由,最好的写作方式可能是:

/* comments here */
if(someFunction1(a)){
   return 0;
} 

/* comments here */
if(someFunction2(b->b1,c)){
 return 0;
}

...

/* if we got here, then there are no errors */
do_something();
如果您需要在错误检查之间执行代码,这还具有可维护的优点。或者,如果您希望将一些更复杂的表达式拆分为几行以提高可读性


尽管在很多情况下,多个return语句可能会创建混乱的代码,但这并不是其中之一。在这种情况下,多个return语句实际上提高了可读性/可维护性。您不应该仅仅因为某些编码标准要求您这样做,就武断地避免使用多个返回语句。

当然,将其分解为例如
const int retval=0
,则在第一个
之前执行code>,然后执行
返回retval根据需要。@unwind:会的。但由于涉及到冗余,因此效率仍然不高。OP将一个单行代码扩展到了近15行:显然,这个问题的可能重复性与个人风格(主要基于观点)有一定的关系。这与主要基于观点有一定的关系,但我认为这个问题对于SO格式来说已经够窄了。此外,我认为程序员需要时不时地发泄这些事情,正如我们可以从所有像这样的线程获得的投票数中看出的那样。所以没有必要关闭这个问题,尽管很可能会出现主观答案。现在,任何问题都会进入热门网络问题列表。。。。另外,答案和问题中所写的没有什么不同,只是它遵循了更好的代码格式化实践。如果这样做,您需要小心。原始代码可能依赖于此代码更改的短路评估。就我个人而言,我不会试图给已经很复杂的表达式添加更复杂的内容。“程序正在通过的管道”。。。“一系列不可中断的操作”。。。当我说编程是一门艺术时,人们仍然嘲笑我……另一方面,你可以在条件之外使用逻辑booelan运算符(
|
&&&
)来实现这种流控制。然而,我强烈建议不要这样做,事实上,我强烈建议在对非平凡函数(即可能失败的函数)的每次调用周围都进行错误检查。如果不需要对每个错误案例进行大量注释,我也会这样做(尽管我将“tube”放在表达式的右侧)。在这种情况下,您需要将代码拆分。@Lundin:我知道一开始听起来有悖常理,但我发现,将and运算符放在左侧有几个优点。我建议试几周/几个月。在过去的两年里,我做了很多crunch阶段的编码,在连续12个小时查看源代码后,你希望它以最容易阅读的方式进行格式化。我对SQL和其他语言使用类似的语义。左边的管道/逗号/和/等可以更容易地注释掉一行,而不会影响流程-我更可能注释掉列表中的最后一行。。。比我先评论一下。如果我注释掉最后一个,管道在右边,它会出错。。。如果管道在左边,它将忽略该线。非常适合快速测试。