Language agnostic 函数应该只有一个return语句吗?

Language agnostic 函数应该只有一个return语句吗?,language-agnostic,coding-style,Language Agnostic,Coding Style,一个函数中只有一个return语句是更好的做法,有没有充分的理由 或者,只要逻辑正确,就可以从函数返回,这意味着函数中可能有许多返回语句?我想说,您应该根据需要使用尽可能多的返回语句,或者任何使代码更干净的语句(例如) 我个人从未听过/见过任何“最佳实践”说您应该只有一份返回声明 在大多数情况下,我倾向于基于逻辑路径尽快退出函数(guard子句就是一个很好的例子)。我能想到的一个很好的理由是代码维护:您只有一个退出点。如果您想更改结果的格式,…,那么实现起来就简单多了。此外,为了进行调试,您可以

一个函数中只有一个return语句是更好的做法,有没有充分的理由


或者,只要逻辑正确,就可以从函数返回,这意味着函数中可能有许多返回语句?

我想说,您应该根据需要使用尽可能多的返回语句,或者任何使代码更干净的语句(例如)

我个人从未听过/见过任何“最佳实践”说您应该只有一份返回声明


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

我能想到的一个很好的理由是代码维护:您只有一个退出点。如果您想更改结果的格式,…,那么实现起来就简单多了。此外,为了进行调试,您可以在那里粘贴一个断点:)


话虽如此,我曾经不得不在一个库中工作,那里的编码标准规定“每个函数一个返回语句”,我发现这相当困难。我编写了大量的数值计算代码,并且经常有“特殊情况”,因此代码很难理解……

我通常在方法的开头有几个语句,以便在“简单”的情况下返回。例如,这:

publicsvoiddostuff(Foo-Foo)
{
如果(foo!=null)
{
...
}
}
。。。可以使其更具可读性(IMHO),如下所示:

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;
}
publicsvoiddostuff(Foo-Foo)
{
if(foo==null)返回;
...
}

因此,是的,我认为从一个函数/方法中有多个“退出点”是可以的。

说每个函数应该只有一个return语句。这是为了限制复杂性。许多人,比如Martin Fowler,认为用多个返回语句编写函数更简单。他在自己写的经典著作中提出了这一论点。如果您遵循他的其他建议并编写小函数,这将非常有效。我同意这一观点,只有严格的结构化编程纯粹主义者才会坚持每个函数都有一个返回语句。

我使用过糟糕的编码标准,这些标准迫使你只能选择一条退出路径,结果几乎总是非结构化的意大利面,如果函数不是那么简单的话——你最终会遇到很多中断和错误我想说,武断地反对多个退出点是非常不明智的,因为我发现这种技术在实践中一次又一次地有用,事实上,为了清晰起见,我经常将现有代码重构为多个退出点。因此,我们可以比较这两种方法:-

stringfoobar(字符串s,int?i){
字符串ret=“”;
如果(!string.IsNullOrEmpty&&i!=null){
var res=某个函数(s,i);
布尔通过=真;
foreach(在res中的var r){
如果(!r.Passed){
通过=错误;
打破
}
}
如果(通过){
//代码的其余部分。。。
}
}
返回ret;
}
将此与允许多个出口点的代码进行比较:-

stringfoobar(字符串s,int?i){
var ret=“”;
if(string.IsNullOrEmpty(s)| | i==null)返回null;
var res=某个函数(s,i);
foreach(在res中的var r){
如果(!r.Passed)返回null;
}
//代码的其余部分。。。
返回ret;
}

我认为后者要清楚得多。据我所知,对多个退出点的批评是一个相当古老的观点。< / P> < P>我在C++的编码标准中看到,这是C的一个遗留问题,好像你没有RAII或其他自动内存管理,那么你必须为每个返回清理,这意味着清理和粘贴清理或Goto。(逻辑上与托管语言中的“finally”相同)如果你的做法是使用C++中的智能指针和集合或另一个自动存储系统,那么就没有一个很强的理由,它就变成了可读性,更多的是判断调用。

< P> >肯特·贝克在讨论一个例程中的保护子句时注意到一个条目。和出口点

“是为了防止可能出现的混乱 当跳进跳出许多 同一程序中的位置。它使 应用于FORTRAN或 汇编语言程序 拥有大量全球数据,甚至 了解哪些陈述是正确的 这是一项艰苦的工作……使用小方法和大部分本地数据,这是不必要的保守。”


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

一般来说,我试着从函数中只有一个退出点。然而,有时这样做实际上会创建一个比必要时更复杂的函数体,在这种情况下,最好是r必须有多个退出点。这确实必须是一个基于结果复杂性的“判断调用”,但目标应该是在不牺牲复杂性和可理解性的情况下尽可能少的退出点。

我目前正在开发一个代码库,其中两名工作人员盲目订阅“单一退出点”理论和我可以告诉你们,从经验来看,这是一个可怕的做法。它使代码极难维护,我将向你们展示原因

使用“单点退出”理论,您不可避免地会得到如下代码:

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之间的操作。您必须缩进整个异常函数,祝您好运,确保所有if/else条件和大括号正确匹配


这种方法使代码维护变得非常困难,而且容易出错。

对于足够小的函数来说,多个退出点是合适的,也就是说,一个函数可以在其屏幕上的一个屏幕长度上查看
if (a > 0)
  return positively(a);
else
  return negatively(a);
resulttype res;
if if if...
return res;
sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}
@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}