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

我总是听说一个单一的退出点函数是一种糟糕的代码编写方式,因为你的可读性和效率都下降了。 我从来没有听到有人认为对方。

我认为这跟CS有点关系,但是这个问题是在cstheory stackexchange上被击落的。

有不同的思想stream派,主要归结为个人喜好。

一个是,如果只有一个退出点,那么它就不那么令人困惑了 – 通过该方法的单一path,您知道在哪里查找退出。 如果使用缩进来表示嵌套,那么在负面方面,代码会大量向右缩进,并且很难跟随所有嵌套的作用域。

另一个原因是你可以检查前提条件,并在方法开始时尽早退出,以便在方法的主体中知道某些条件是真实的,而不是将方法的整个主体向右缩进5英里。 这通常会最大限度地减less您必须担心的范围数量,这使得代码更容易遵循。

三分之一是你可以在任何地方退出。 过去,这个问题以前比较混乱,但现在我们已经有语法着色的编辑器和编译器来检测不可达的代码,处理起来要容易得多。

我正在中间阵营。 实施一个单一的退出点是一个毫无意义,甚至适得其反的限制恕我直言,随机退出所有的一种方法有时会导致杂乱难以遵循的逻辑,很难看到一个给定的位代码是否会或不会执行。 但是“门”你的方法可以显着简化方法的主体。

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

  如果(!参数)//检查是否非空
    返回ERR_NULL_ARGUMENT;
   ...处理非空参数
  如果(好)
    返回0;
  其他
    返回ERR_NOT_OK;

比:

   int return_value;
  如果(参数)//非空
   {
    进程非空参数
     ..适当地设置结果
   }
  其他
    结果= ERR_NULL_ARGUMENT;
  返回结果;

如果某个条件应该阻止某个函数做任何事情,那么我宁愿早点返回函数以外的地方。 一旦该function采取了带有副作用的行动,我宁愿从底部回来,以明确所有的副作用必须处理。

单一的入口和出口点是结构化编程的原始概念,是一步一步的意大利面编码(Spaghetti Coding)。 有一种观点认为,多个退出点函数需要更多的代码,因为你必须适当地清理分配给variables的内存空间。 考虑一个场景,其中函数分配variables(资源)和提前离开函数,没有适当的清理会导致资源泄漏。 另外,在每个出口前进行清理会产生大量的冗余代码。

如果你觉得你需要在一个函数中有多个退出点,那么这个函数太大了,而且做的太多了。

我会推荐阅读罗伯特·C·马丁(Robert C. Martin)着作“清洁代码”(Clean Code)中有关函数的章

本质上,你应该尝试用4行或更less的代码编写函数。

Mike Long博客的一些笔记:

  • function的第一个规则是:它们应该很小
  • function的第二个规则是:它们应该小于那个
  • if语句中的块,while语句,for循环等应该是一行
  • …和那行代码通常是一个函数调用
  • 应该不超过一个或两个级别的缩进
  • 函数应该做一件事
  • 函数语句应该都处于相同的抽象层次
  • 一个函数应该有不超过3个参数
  • 输出参数是代码味道
  • 将一个布尔标志传递给一个函数真的很糟糕。 按照定义,你在function上做了两件事。
  • 副作用是谎言。

大多数情况下,这都归结于可交付成果的需求。 在“过去”,意大利面代码与多个返回点邀请内存泄漏,因为编码器,首选方法通常不清理。 还有一些编译器在返回期间popup堆栈时,会丢失对返回variables的引用,在从嵌套作用域返回的情况下。 更常见的问题是重入代码之一,它尝试使函数的调用状态与返回状态完全相同。 oop的突变体侵犯了这一点,这个概念被搁置了。

有交付物,最值得注意的是内核,它需要多个出口点提供的速度。 这些环境通常有自己的内存和stream程pipe理,所以泄漏的风险最小化。

就我个人而言,我喜欢单点退出,因为我经常用它在return语句中插入一个断点,并对代码如何确定解决scheme进行代码检查。 我可以进入入口,并通过广泛的嵌套和recursion解决scheme。 作为代码审查人员,函数中的多个返回需要更深入的分析 – 所以如果你正在做这个工作来加速实现,那么你就是在抢劫Peter来拯救Paul。 代码审查需要更多的时间,推断有效实施的推定。

– 2美分

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

在我看来,在一个点上退出一个函数(或其他控制结构)的build议往往是超卖的。 通常只有两个理由退出:

  1. 单退出代码据说更容易阅读和debugging。 (我承认,我不这么认为,但是给出了这样的结论,读取和debugging大体上更容易一些。
  2. 单退出代码链接和返回更干净。

第二个原因是微妙的,有一些优点,特别是如果函数返回一个大的数据结构。 不过,我不会担心这个,除非…

如果是学生,你想在class上获得最高分。 做什么教练喜欢。 从他的angular度来看,他可能有一个很好的理由。 所以,至less,你会学到他的观点。 这本身就有价值。

祝你好运。

我曾经是一个单一退出风格的倡导者。 我的推理主要来自痛苦…

单出口更容易debugging。

鉴于我们今天所使用的技术和工具,作为unit testing来说这是一个不太合理的位置,并且日志logging可以使单一出口变得不必要。 也就是说,当你需要在debugging器中执行代码时,就很难理解和使用包含多个退出点的代码。

当你为了检查状态而需要插入任务时(尤其是在现代debugging器中用监视expression式replace),情况尤其如此。 以隐藏问题或完全破坏执行的方式来改变控制stream程也是非常容易的。

单一退出方法在debugging器中更容易一步完成,而且更容易在不破坏逻辑的情况下分离。

答案是非常依赖于上下文的。 如果你正在制作一个graphics用户界面(GUI),并有一个初始化API的函数,并在你的主开始时打开窗口,它将会充满可能抛出错误的调用,每个调用都会导致程序的实例closures。 如果你使用嵌套的IF语句并缩进,你的代码很快就会变得非常向右倾斜。 在每个阶段返回一个错误可能会更好,实际上更易于阅读,同时在代码中使用几个标志也很容易debugging。

但是,如果您正在testing不同的条件并根据您的方法中的结果返回不同的值,那么有一个单一的退出点可能会更好。 我曾经在MATLAB中处理image processing脚本,可能会变得非常大。 多个退出点可能使代码非常难以遵循。 开关语句更合适。

要做的最好的事情就是随时学习。 如果您正在编写代码,请尝试查找其他人的代码并查看其实现方式。 决定你喜欢哪个位,哪些位不是。