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

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


当前回答

正如Kent Beck在《实现模式》中讨论保护子句时指出的那样,使一个例程具有单一的入口和出口点……

是为了防止可能的混淆 当你在许多地方跳来跳去 同一例程中的位置。这让 当应用到FORTRAN或 汇编语言程序 有大量的全球数据,甚至 理解哪些语句是 执行是一项艰苦的工作……使用小方法和大部分本地数据,它是不必要的保守。”

我发现用保护子句编写的函数要比一长串嵌套的if then else语句容易理解得多。

其他回答

我想说的是,你应该有尽可能多的需要,或者任何使代码更干净的(如保护子句)。

我个人从来没有听过/见过任何“最佳实践”说你应该只有一个返回语句。

在大多数情况下,我倾向于根据逻辑路径尽快退出函数(保护子句就是一个很好的例子)。

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语句,特别是如果您正在返回一个值,则很难做到这一点。

我倾向于认为函数中间的return语句是不好的。您可以使用返回在函数的顶部构建一些保护子句,当然也可以告诉编译器在函数的末尾返回什么,但函数中间的返回很容易被忽略,并且会使函数更难解释。

使用单一的退出点有好处,就像不可避免的“箭头”编程也有坏处一样。

如果在输入验证或资源分配期间使用多个出口点,我尝试将所有“错误出口”非常明显地放在函数的顶部。

“SSDSLPedia”的Spartan Programming文章和“Portland Pattern Repository’s Wiki”的单一函数出口点文章都对此有一些深刻的争论。当然,还有这篇文章要考虑。

例如,如果您真的需要一个单一的退出点(在任何不支持异常的语言中),以便在一个地方释放资源,我发现仔细应用goto是很好的;请看这个相当做作的例子(压缩以节省屏幕空间):

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

就我个人而言,总的来说,我不喜欢箭头编程,而不喜欢多个出口点,尽管两者在正确应用时都很有用。当然,最好的方法是将程序的结构设计成两者都不需要。将你的函数分解成多个块通常会有所帮助:)

尽管在这样做的时候,我发现我最终得到了多个出口点,就像在这个例子中,一些较大的函数被分解成几个较小的函数:

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

根据项目或编码指南的不同,大多数样板代码可以被宏替换。作为旁注,这样分解可以使函数g0、g1、g2非常容易单独测试。

显然,在面向对象和支持异常的语言中,我不会使用这样的if语句(或者根本不会使用,如果我可以用足够少的努力就能摆脱它的话),代码将更加简单。和non-arrowy。大多数非最终回报可能是例外。

简而言之,

少回报比多回报好 多于一次的返回比巨大的箭头更好,并且保护条款通常是可以的。 在可能的情况下,例外可能/应该取代大多数“保护条款”。

作为指导原则,我投票支持单次返回。这有助于一个常见的代码清理处理…例如,看看下面的代码…

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}