当代码流是这样的:

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

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

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

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

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


当前回答

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

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

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

其他回答

TLDR: RAII、事务性代码(仅设置结果或在已经计算时返回内容)和异常。

长一点的回答:

在C语言中,这类代码的最佳实践是在代码中添加一个EXIT/CLEANUP/other标签,在该标签中进行本地资源的清理,并返回错误代码(如果有的话)。这是最佳实践,因为它将代码自然地划分为初始化、计算、提交和返回:

error_code_type c_to_refactor(result_type *r)
{
    error_code_type result = error_ok; //error_code_type/error_ok defd. elsewhere
    some_resource r1, r2; // , ...;
    if(error_ok != (result = computation1(&r1))) // Allocates local resources
        goto cleanup;
    if(error_ok != (result = computation2(&r2))) // Allocates local resources
        goto cleanup;
    // ...

    // Commit code: all operations succeeded
    *r = computed_value_n;
cleanup:
    free_resource1(r1);
    free_resource2(r2);
    return result;
}

在C语言中,在大多数代码库中,if(error_ok !=…goto代码通常隐藏在一些方便的宏(RET(computation_result), ENSURE_SUCCESS(computation_result, return_code)等)后面。

c++比C提供了额外的工具:

清理块功能可以作为RAII实现,这意味着您不再需要整个清理块,并允许客户端代码添加早期返回语句。 当无法继续时抛出抛出,转换所有if(error_ok !=…变成直接的通话。

等价的c++代码:

result_type cpp_code()
{
    raii_resource1 r1 = computation1();
    raii_resource2 r2 = computation2();
    // ...
    return computed_value_n;
}

这是最佳实践,因为:

It is explicit (that is, while error handling is not explicit, the main flow of the algorithm is) It is straightforward to write client code It is minimal It is simple It has no repetitive code constructs It uses no macros It doesn't use weird do { ... } while(0) constructs It is reusable with minimal effort (that is, if I want to copy the call to computation2(); to a different function, I don't have to make sure I add a do { ... } while(0) in the new code, nor #define a goto wrapper macro and a cleanup label, nor anything else).

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

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

// edit 
doThingsAtEndAndReportErrorStatus()

I'm adding an answer for the sake of completeness. A number of other answers pointed out that the large condition block could be split out into a separate function. But as was also pointed out a number of times is that this approach separates the conditional code from the original context. This is one reason that lambdas were added to the language in C++11. Using lambdas was suggested by others but no explicit sample was provided. I've put one in this answer. What strikes me is that it feels very similar to the do { } while(0) approach in many ways - and maybe that means it's still a goto in disguise....

earlier operations
...
[&]()->void {

    if (!check()) return;
    ...
    ...
    if (!check()) return;
    ...
    ...
    if (!check()) return;
    ...
    ...
}();
later operations

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

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

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

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

    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;
    }