为什么函数只有一个退出点?

hex*_*omb 89 coding-style

我一直听说单个退出点功能是一种糟糕的代码方式,因为你失去了可读性和效率.我从来没有听到任何人争辩对方.

我认为这与CS有关,但这个问题在cstheory stackexchange中被击落.

Jas*_*ams 106

有不同的思想流派,主要归结为个人偏好.

一个是如果只有一个出口点就不那么容易混淆了 - 你有一条通过这个方法的路径,你知道在哪里寻找出口.如果你使用缩进来表示嵌套,那么你的代码会在右侧大量缩进,并且跟随所有嵌套作用域变得非常困难.

另一个是您可以检查前提条件并在方法开始时提前退出,这样您就可以在方法体中知道某些条件为真,而不会使方法的整个主体向右缩进5英里.这通常可以最大限度地减少您需要担心的范围数量,从而使代码更容易理解.

第三是您可以随意退出.在过去,这曾经更加令人困惑,但现在我们有语法着色编辑器和编译器来检测无法访问的代码,因此处理起来要容易得多.

我正好在中间营地.执行单个退出点是一种毫无意义甚至适得其反的限制恕我直言,而随机退出一个方法有时会导致混乱难以遵循逻辑,在那里很难看出给定的代码是否会或将不会执行.但是"选通"你的方法可以显着简化方法的主体.

  • 在“singe exit”范式中,可以通过“go to”语句避免深度嵌套。此外,人们还有机会在函数的本地“Error”标签下执行一些后处理,这对于多个“return”来说是不可能的。 (2认同)
  • 通常有一个很好的解决方案可以避免需要去。我更喜欢'return(Fail(...))'并将共享的清理代码放入Fail方法中。这可能需要传入一些本地变量以允许释放内存等,但除非您处于性能关键的代码位中,否则这通常是比 goto IMO 更清晰的解决方案。它还允许多种方法共享类似的清理代码。 (2认同)

sup*_*cat 37

我的一般建议是,在可行的情况下,返回语句应位于具有任何副作用的第一个代码之前,或者位于具有任何副作用的最后一个代码之后.我会考虑这样的事情:

  if (!argument)  // Check if non-null
    return ERR_NULL_ARGUMENT;
  ... process non-null argument
  if (ok)
    return 0;
  else
    return ERR_NOT_OK;

比:更清楚:

  int return_value;
  if (argument) // Non-null
  {
    .. process non-null argument
    .. set result appropriately
  }
  else
    result = ERR_NULL_ARGUMENT;
  return result;

如果某个条件应该阻止函数做任何事情,我宁愿在函数执行任何操作的点之上的某个位置提前返回函数.然而,一旦该功能采取了具有副作用的行动,我宁愿从底部返回,以明确必须处理所有副作用.

  • @GuidoG:这两种模式可能更具可读性,具体取决于省略的部分中显示的内容.使用"return x;" 清楚地表明,如果达到该语句,返回值将为x.使用"result = x;" 在它返回之前,它会打开其他东西可能会改变结果的可能性.如果它实际上有必要改变结果,这可能是有用的,但是检查代码的程序员必须检查它以查看结果可能如何改变,即使答案是"它不能". (5认同)
  • 第一个示例在执行所有操作的所有代码之前的开头都有一个“ return”。至于使用`?:`运算符,将其写在单独的行上可以使许多IDE在将调试断点附加到不正常的情况下变得更加容易。顺便说一句,“单个出口点”的“真实”键在于理解重要的是,对于每个对正常函数的特定调用,出口点是“该调用之后的点”。如今的程序员认为这是理所当然的,但事情并非总是如此。在极少数情况下,代码可能必须没有堆栈空间才能通过,从而导致函数... (2认同)
  • 所谓的更清晰的例子是不那么清晰和难以阅读。一个出口点总是更容易阅读、更容易维护、更容易调试。 (2认同)

PSS*_*PSS 14

单个入口和出口点是结构化编程的原始概念与逐步的Spaghetti Coding.有一种观点认为,多个退出点函数需要更多代码,因为您必须正确清理为变量分配的内存空间.考虑一个场景,其中函数分配变量(资源)并且尽早退出函数并且没有适当的清理会导致资源泄漏.此外,在每次退出之前构建清理会产生大量冗余代码.


ssc*_*der 13

对于大多数事情,它都归结为可交付成果的需求.在"过去的日子"中,带有多个返回点的意大利面条代码会引起内存泄漏,因为喜欢该方法的编码器通常不能很好地清理.在从嵌套范围返回的情况下,当返回期间弹出堆栈时,某些编译器还"丢失"对返回变量的引用也存在问题.更普遍的问题是重入代码,它试图使函数的调用状态与其返回状态完全相同.oop的变种人违反了这一点,这个概念被搁置了.

有可交付成果,尤其是内核,需要多个出口点提供的速度.这些环境通常具有自己的内存和进程管理,因此泄漏的风险最小化.

就个人而言,我喜欢单点退出,因为我经常使用它在return语句中插入断点并执行代码检查代码如何确定该解决方案.我可以进入入口并逐步完成,我使用广泛的嵌套和递归解决方案.作为代码审阅者,函数中的多个返回需要更深入的分析 - 所以如果你这样做是为了加快实现,那么你就是在抢劫彼得以拯救保罗.代码审查需要更多时间,使有效实施的假设无效.

- 2美分

有关详细信息,请参阅此文档:NISTIR 5459

  • `函数中的多个返回只需要功能很大(> 1个屏幕)就需要更深入的分析,否则会使分析更容易 (6认同)
  • 多重回报永远不会使分析更容易,只是相反 (3认同)

小智 6

我曾经是单出口风格的倡导者。我的推理主要来自痛苦......

单出口更容易调试。

考虑到我们今天拥有的技术和工具,这是一个不太合理的位置,因为单元测试和日志记录可以使单退出变得不必要。也就是说,当您需要在调试器中观察代码执行时,理解和处理包含多个退出点的代码要困难得多。

当您需要插入赋值以检查状态(在现代调试器中替换为监视表达式)时,这一点尤其正确。以隐藏问题或完全中断执行的方式更改控制流也太容易了。

单出口方法更容易在调试器中逐步执行,并且更容易在不破坏逻辑的情况下梳理。