是否有充分的理由说明在函数中只有一个return语句是一个更好的实践?

或者在逻辑上正确的情况下,可以从函数中返回,这意味着函数中可能有许多返回语句?


当前回答

一般来说,我尝试从一个函数中只有一个出口点。然而,有时这样做实际上会创建一个比必要的更复杂的函数体,在这种情况下,最好有多个出口点。它确实需要基于结果的复杂性进行“判断”,但目标应该是在不牺牲复杂性和可理解性的情况下尽可能减少出口点。

其他回答

我使用多个出口点使错误情况+处理+返回值尽可能接近。

所以必须测试条件a, b, c必须为真,你需要用不同的方式处理它们:

if (a is false) {
    handle this situation (eg. report, log, message, etc.)
    return some-err-code
}
if (b is false) {
    handle this situation
    return other-err-code
}
if (c is false) {
    handle this situation
    return yet-another-err-code
}

perform any action assured that a, b and c are ok.

a, b和c可能是不同的东西,比如a是输入参数检查,b是新分配内存的指针检查,c是检查参数a中的值。

Well, maybe I'm one of the few people here old enough to remember one of the big reasons why "only one return statement" was pushed so hard. It's so the compiler can emit more efficient code. For each function call, the compiler typically pushes some registers on the stack to preserve their values. This way, the function can use those registers for temporary storage. When the function returns, those saved registers have to be popped off the stack and back into the registers. That's one POP (or MOV -(SP),Rn) instruction per register. If you have a bunch of return statements, then either each one has to pop all the registers (which makes the compiled code bigger) or the compiler has to keep track of which registers might have been modified and only pop those (decreasing code size, but increasing compilation time).

今天仍然坚持使用一个return语句的一个原因是易于自动重构。如果您的IDE支持方法提取重构(选择一系列行并将它们转换为一个方法),那么如果您想提取的行中有一个return语句,特别是如果您正在返回一个值,则很难做到这一点。

我想说,武断地决定不使用多个出口点是非常不明智的,因为我发现这种技术在实践中一次又一次地有用,事实上,为了清晰起见,我经常将现有的代码重构为多个出口点。我们可以这样比较这两种方法:-

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

将此与允许多个出口点的代码进行比较

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

我认为后者要清楚得多。据我所知,现在对多个退出点的批评是一种相当过时的观点。

I've seen it in coding standards for C++ that were a hang-over from C, as if you don't have RAII or other automatic memory management then you have to clean up for each return, which either means cut-and-paste of the clean-up or a goto (logically the same as 'finally' in managed languages), both of which are considered bad form. If your practices are to use smart pointers and collections in C++ or another automatic memory system, then there isn't a strong reason for it, and it become all about readability, and more of a judgement call.

如果管理得当,多次退出是件好事

第一步是明确退出的原因。我的通常是这样的: 1. 不需要执行函数 2. 发现错误 3.提前完成 4. 正常完成 我想你可以把“1”归为一类。不需要执行函数“into”3。提前完成”(如果你愿意的话,可以说是非常提前完成)。

第二步是让函数外部的人知道退出的原因。伪代码看起来像这样:

function foo (input, output, exit_status)

  exit_status == UNDEFINED
  if (check_the_need_to_execute == false) then
    exit_status = NO_NEED_TO_EXECUTE  // reason #1 
    exit

  useful_work

  if (error_is_found == true) then
    exit_status = ERROR               // reason #2
    exit
  if (need_to_go_further == false) then
    exit_status = EARLY_COMPLETION    // reason #3
    exit

  more_work

  if (error_is_found == true) then
    exit_status = ERROR
  else
    exit_status = NORMAL_COMPLETION   // reason #4

end function

显然,如果将上图中的一大块工作移到一个单独的函数中是有益的,那么您应该这样做。

如果您愿意,您可以使用更具体的退出状态,例如,使用几个错误代码和早期完成代码来精确定位退出的原因(甚至位置)。

即使您强制这个函数只有一个出口,我认为您仍然需要指定出口状态。调用者需要知道是否可以使用输出,这有助于维护。