是否有充分的理由说明在函数中只有一个return语句是一个更好的实践?
或者在逻辑上正确的情况下,可以从函数中返回,这意味着函数中可能有许多返回语句?
是否有充分的理由说明在函数中只有一个return语句是一个更好的实践?
或者在逻辑上正确的情况下,可以从函数中返回,这意味着函数中可能有许多返回语句?
当前回答
Structured programming says you should only ever have one return statement per function. This is to limit the complexity. Many people such as Martin Fowler argue that it is simpler to write functions with multiple return statements. He presents this argument in the classic refactoring book he wrote. This works well if you follow his other advice and write small functions. I agree with this point of view and only strict structured programming purists adhere to single return statements per function.
其他回答
我想说的是,你应该有尽可能多的需要,或者任何使代码更干净的(如保护子句)。
我个人从来没有听过/见过任何“最佳实践”说你应该只有一个返回语句。
在大多数情况下,我倾向于根据逻辑路径尽快退出函数(保护子句就是一个很好的例子)。
使用单一的退出点有好处,就像不可避免的“箭头”编程也有坏处一样。
如果在输入验证或资源分配期间使用多个出口点,我尝试将所有“错误出口”非常明显地放在函数的顶部。
“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。大多数非最终回报可能是例外。
简而言之,
少回报比多回报好 多于一次的返回比巨大的箭头更好,并且保护条款通常是可以的。 在可能的情况下,例外可能/应该取代大多数“保护条款”。
作为嵌套if的替代方法,有一种使用do/while(false)在任何地方爆发的方法:
function()
{
HRESULT error = S_OK;
do
{
if(!SUCCEEDED(Operation1()))
{
error = OPERATION1FAILED;
break;
}
if(!SUCCEEDED(Operation2()))
{
error = OPERATION2FAILED;
break;
}
if(!SUCCEEDED(Operation3()))
{
error = OPERATION3FAILED;
break;
}
if(!SUCCEEDED(Operation4()))
{
error = OPERATION4FAILED;
break;
}
} while (false);
return error;
}
这为您提供了一个出口点,允许您进行其他嵌套操作,但仍然不是真正的深层结构。如果你不喜欢成功,你可以做任何失败的事情。这类事情还允许您在任意两个其他检查之间添加其他代码,而无需重新缩进任何内容。
如果你真的疯了,整个If块也可以宏化。: D
#define BREAKIFFAILED(x,y) if (!SUCCEEDED((x))) { error = (Y); break; }
do
{
BREAKIFFAILED(Operation1(), OPERATION1FAILED)
BREAKIFFAILED(Operation2(), OPERATION2FAILED)
BREAKIFFAILED(Operation3(), OPERATION3FAILED)
BREAKIFFAILED(Operation4(), OPERATION4FAILED)
} while (false);
Structured programming says you should only ever have one return statement per function. This is to limit the complexity. Many people such as Martin Fowler argue that it is simpler to write functions with multiple return statements. He presents this argument in the classic refactoring book he wrote. This works well if you follow his other advice and write small functions. I agree with this point of view and only strict structured programming purists adhere to single return statements per function.
I lean towards using guard clauses to return early and otherwise exit at the end of a method. The single entry and exit rule has historical significance and was particularly helpful when dealing with legacy code that ran to 10 A4 pages for a single C++ method with multiple returns (and many defects). More recently, accepted good practice is to keep methods small which makes multiple exits less of an impedance to understanding. In the following Kronoz example copied from above, the question is what occurs in //Rest of code...?:
void string fooBar(string s, int? i) {
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;
}
我意识到这个例子有点做作,但我很想把foreach循环重构成一个LINQ语句,然后将其视为一个保护子句。同样,在一个人为的例子中,代码的意图并不明显,someFunction()可能会有一些其他副作用,或者结果可能会在代码的// Rest中使用....
if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;
给出以下重构函数:
void string fooBar(string s, int? i) {
if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;
// Rest of code...
return ret;
}