50 个答案:
答案 0 :(得分:742)
我经常在一个方法的开头有几个语句来返回“简单”的情况。例如,这个:
public void DoStuff(Foo foo)
{
if (foo != null)
{
...
}
}
...可以像这样更具可读性(恕我直言):
public void DoStuff(Foo foo)
{
if (foo == null) return;
...
}
所以是的,我认为从函数/方法中获得多个“退出点”是可以的。
答案 1 :(得分:355)
没有人提及或引用Code Complete所以我会这样做。
17.1返回
最大限度地减少每个例行程序的退货次数。理解一个例程是比较困难的,如果从底部读到它,你就不会意识到它返回到某个地方的可能性。
在增强可读性时使用返回。在某些例程中,一旦知道答案,就要立即将其返回到调用例程。如果例程的定义方式不需要任何清理,则不立即返回意味着您必须编写更多代码。
答案 2 :(得分:229)
我会说任意对多个退出点进行任意决定是非常不明智的,因为我发现该技术在实践中一遍又一遍地有用,事实上我经常重构为清晰起见,现有代码到多个出口点。我们可以比较这两种方法: -
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;
}
我认为后者相当清楚。据我所知,多个退出点的批评现在是一个相当陈旧的观点。
答案 3 :(得分:191)
我目前正在开发一个代码库,其中两个工作的人盲目地订阅了“单点退出”理论,我可以告诉你,根据经验,这是一个可怕的可怕做法。它使代码非常难以维护,我会告诉你原因。
使用“单点退出”理论,您不可避免地会遇到如下代码:
function()
{
HRESULT error = S_OK;
if(SUCCEEDED(Operation1()))
{
if(SUCCEEDED(Operation2()))
{
if(SUCCEEDED(Operation3()))
{
if(SUCCEEDED(Operation4()))
{
}
else
{
error = OPERATION4FAILED;
}
}
else
{
error = OPERATION3FAILED;
}
}
else
{
error = OPERATION2FAILED;
}
}
else
{
error = OPERATION1FAILED;
}
return error;
}
这不仅使代码难以理解,而且现在稍后说你需要返回并在1到2之间添加一个操作。你必须缩进整个freaking函数,并且好运确保所有if / else条件和大括号都匹配正确。
此方法使代码维护极其困难且容易出错。
答案 4 :(得分:72)
Structured programming说你应该每个函数只有一个return语句。这是为了限制复杂性。许多人如Martin Fowler认为用多个return语句编写函数更简单。他在他写的经典refactoring书中提出了这一论点。如果你遵循他的其他建议并编写小函数,这很有效。我同意这种观点,只有严格的结构化编程纯粹主义者遵守每个函数的单个返回语句。
答案 5 :(得分:62)
正如Kent Beck在Implementation Patterns中讨论保护条款时所说的那样,例程中只有一个入口和出口点......
“是为了防止混乱
当进出许多人时
同一例程中的位置。它做了
应用于FORTRAN时的良好意义
编写汇编语言程序
甚至还有很多全球数据
了解哪些陈述
执行起来很辛苦......用小方法和大多数本地数据,这是不必要的保守。“
我发现一个用guard子句编写的函数比一组长的if then else语句更容易理解。
答案 6 :(得分:61)
在没有副作用的函数中,没有充分的理由要有多个返回,你应该用函数式编写它们。在具有副作用的方法中,事物更顺序(时间索引),因此您使用return语句作为命令停止执行,以命令式样式编写。
换句话说,如果可能的话,赞成这种风格
return a > 0 ?
positively(a):
negatively(a);
这个
if (a > 0)
return positively(a);
else
return negatively(a);
如果您发现自己编写了几层嵌套条件,那么可能有一种方法可以使用谓词列表进行重构。如果您发现ifs和elses在语法上相距甚远,您可能希望将其分解为更小的函数。一个超过一屏文本的条件块很难阅读。
没有适用于每种语言的硬性规则。像一个返回语句之类的东西不会使你的代码变好。但好的代码往往会让你以这种方式编写函数。
答案 7 :(得分:43)
我已经在C ++的编码标准中看到过,这是C语言的遗留问题,好像你没有RAII或其他自动内存管理那么你必须为每次返回进行清理,这或者意味着切换 - 并且 - 粘贴清理或goto(逻辑上与托管语言中的'finally'相同),这两种都被认为是不良形式。如果你的做法是在C ++或其他自动内存系统中使用智能指针和集合,那么就没有强有力的理由,它就变成了可读性和更多的判断调用。
答案 8 :(得分:40)
我倾向于认为函数的middle中的return语句是坏的。你可以使用return在函数的顶部构建一些guard子句,当然告诉编译器在函数结束时返回什么没有问题,但返回函数的middle可能容易错过,并且可能使函数难以解释。
答案 9 :(得分:38)
为什么在函数中只有一个return语句更好的做法有充分的理由吗?
是,有:
单一出口点是确定后置条件的绝佳场所。
能够在函数末尾的一个返回上放置调试器断点通常很有用。
更少的回报意味着更少的复杂性。线性代码通常更容易理解。
如果尝试将函数简化为单一返回会导致复杂性,那么这就是重构更小,更通用,更易于理解的函数的动机。
如果您使用的语言没有析构函数或者您不使用RAII,那么单次返回会减少您需要清理的地点数量。
某些语言需要一个退出点(例如,Pascal和Eiffel)。
这个问题通常被认为是多个回报或深度嵌套的if语句之间的错误二分法。几乎总有第三种解决方案非常线性(没有深度嵌套),只有一个出口点。
要清楚,我并不是说多次返回总是错误。但是考虑到其他方面的等效解决方案,有很多理由可以选择单一回报。
答案 10 :(得分:33)
答案 11 :(得分:19)
一般来说,我尝试只从一个函数中获得一个退出点。然而,有时候这样做实际上最终会创建一个比必要的更复杂的函数体,在这种情况下,最好有多个出口点。它必须是基于由此产生的复杂性的“判断调用”,但目标应该是尽可能少的退出点而不牺牲复杂性和可理解性。
答案 12 :(得分:14)
(除了具有例外的语言中的任何多行功能无论如何都会有多个退出点这一事实。)
答案 13 :(得分:14)
我的偏好是单退出,除非它真的让事情复杂化。我发现在某些情况下,多个存在点可以掩盖其他更重要的设计问题:
public void DoStuff(Foo foo)
{
if (foo == null) return;
}
看到这段代码后,我会马上问:
'foo'永远是空的吗?
如果是这样,“DoStuff”有多少客户端使用null'foo'调用该函数?
根据这些问题的