当代码流是这样的:

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

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

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

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

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


当前回答

如果根据故障发生的位置需要不同的清理步骤,另一个模式很有用:

    private ResultCode DoEverything()
    {
        ResultCode processResult = ResultCode.FAILURE;
        if (DoStep1() != ResultCode.SUCCESSFUL)
        {
            Step1FailureCleanup();
        }
        else if (DoStep2() != ResultCode.SUCCESSFUL)
        {
            Step2FailureCleanup();
            processResult = ResultCode.SPECIFIC_FAILURE;
        }
        else if (DoStep3() != ResultCode.SUCCESSFUL)
        {
            Step3FailureCleanup();
        }
        ...
        else
        {
            processResult = ResultCode.SUCCESSFUL;
        }
        return processResult;
    }

其他回答

如果对所有错误使用相同的错误处理程序,并且每一步返回一个bool值指示成功:

if(
    DoSomething() &&
    DoSomethingElse() &&
    DoAThirdThing() )
{
    // do good condition action
}
else
{
    // handle error
}

(类似于tyzoid的回答,但条件是动作,&&防止在第一次失败后发生额外的动作。)

也许就像这样

#define EVER ;;

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

或者使用异常

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

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

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

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

...

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

在函数中隔离这些决策并使用返回而不是中断被认为是可以接受的实践。虽然所有这些检查都对应于与函数相同的抽象级别,但这是一种非常符合逻辑的方法。

例如:

void foo(...)
{
   if (!condition)
   {
      return;
   }
   ...
   if (!other condition)
   {
      return;
   }
   ...
   if (!another condition)
   {
      return;
   }
   ... 
   if (!yet another condition)
   {
      return;
   }
   ...
   // Some unconditional stuff       
}

代码流本身已经是一个代码气味,在函数中发生了太多的事情。如果没有直接的解决方案(函数是一个通用的检查函数),那么使用RAII,这样您就可以返回,而不是跳转到函数的结束部分,可能会更好。