当代码流是这样的:

if(check())
{
  ...
  ...
  if(check())
  {
    ...
    ...
    if(check())
    {
      ...
      ...
    }
  }
}

我通常看到这种方法可以避免上述混乱的代码流:

do {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
} while(0);

有什么更好的方法可以避免这种工作/黑客,从而使其成为更高级别(行业级别)的代码?

任何跳出盒子的建议都是欢迎的!


当前回答

我不是特别喜欢在这种情况下使用break或return的方法。考虑到通常当我们面对这样的情况时,这通常是一个比较长的方法。

如果我们有多个出口点,当我们想要知道什么会导致某些逻辑被执行时,这可能会造成困难:通常情况下,我们只是继续向上包含该逻辑的块,而这些块的标准告诉我们情况:

例如,

if (conditionA) {
    ....
    if (conditionB) {
        ....
        if (conditionC) {
            myLogic();
        }
    }
}

通过查看封闭块,很容易发现myLogic()只在条件a和条件b和条件c为真时发生。

当有早期的回报时,它变得不那么明显:

if (conditionA) {
    ....
    if (!conditionB) {
        return;
    }
    if (!conditionD) {
        return;
    }
    if (conditionC) {
        myLogic();
    }
}

我们不能再从myLogic()向上导航,查看封闭块来找出条件。

我使用了不同的变通方法。这是其中之一:

if (conditionA) {
    isA = true;
    ....
}

if (isA && conditionB) {
    isB = true;
    ...
}

if (isB && conditionC) {
    isC = true;
    myLogic();
}

(当然欢迎用同一个变量替换所有isA isB isC)

这样的方法至少会让代码的读者知道,当isB && conditionC时执行myLogic()。给读者一个提示,他需要进一步查找使isB为真的原因。

其他回答

类似于dasblinkenlight的答案,但避免了if语句中的赋值,这可能会被代码评审员“修复”:

bool goOn = check0();
if (goOn) {
    ...
    goOn = check1();
}
if (goOn) {
    ...
    goOn = check2();
}
if (goOn) {
    ...
}

...

当一个步骤的结果需要在下一步之前检查时,我使用这种模式,这与所有检查都可以在前面用一个大if(check1() && check2()…类型的模式。

如何移动if语句到一个额外的函数,产生一个数值或enum结果?

int ConditionCode (void) {
   if (condition1)
      return 1;
   if (condition2)
      return 2;
   ...
   return 0;
}


void MyFunc (void) {
   switch (ConditionCode ()) {
      case 1:
         ...
         break;

      case 2:
         ...
         break;

      ...

      default:
         ...
         break;
   }
}

你可以使用一个简单的bool变量的延续模式:

bool goOn;
if ((goOn = check0())) {
    ...
}
if (goOn && (goOn = check1())) {
    ...
}
if (goOn && (goOn = check2())) {
    ...
}
if (goOn && (goOn = check3())) {
    ...
}

这个执行链将在checkN返回false时停止。由于&&操作符短路,将不再执行进一步的检查…()调用。此外,优化编译器足够聪明,可以识别将goOn设置为false是单行道,并为您插入缺少的goto结尾。因此,上面代码的性能将与do/while(0)相同,只是对其可读性没有严重的影响。

也许就像这样

#define EVER ;;

for(EVER)
{
    if(!check()) break;
}

或者使用异常

try
{
    for(;;)
        if(!check()) throw 1;
}
catch()
{
}

使用异常还可以传递数据。

如果你不需要在执行过程中引入局部变量,那么你通常可以将其平化:

if (check()) {
  doStuff();
}  
if (stillOk()) {
  doMoreStuff();
}
if (amIStillReallyOk()) {
  doEvenMore();
}

// edit 
doThingsAtEndAndReportErrorStatus()