it-swarm.cn

我应该尽早从函数返回还是使用if语句?

我经常以两种格式编写这种函数,并且我想知道一种格式是否比另一种格式更受青睐,为什么。

public void SomeFunction(bool someCondition)
{
    if (someCondition)
    {
        // Do Something
    }
}

要么

public void SomeFunction(bool someCondition)
{
    if (!someCondition)
        return;

    // Do Something
}

我通常使用第一个编码,因为这是我的大脑在编码时的工作方式,尽管我认为我喜欢第二个编码,因为它可以立即处理所有错误,而且我觉得它更易于阅读

302
Rachel

我喜欢第二种风格。首先获取无效的案例,或者简单地退出或根据情况引发异常,在其中插入空白行,然后添加方法的“真实”主体。我发现它更容易阅读。

400
Mason Wheeler

绝对是后者。前者现在看起来还不错,但是当您获得更复杂的代码时,我无法想象有人会这样认为:

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}

我完全承认,我从未理解过单个出口点的优势。

169
Jason Viers

这取决于-通常,我不会竭尽全力尝试移动一堆代码以尽早脱离该功能-编译器通常会为我解决这一问题。话虽这么说,但是如果我需要顶部的一些基本参数,否则我将无法继续,我将尽早进行突破。同样,如果一个条件在函数中生成一个巨大的if块,我也会因此而提前退出。

尽管如此,如果函数在调用时需要一些数据,我通常会抛出一个异常(参见示例),而不是仅仅返回。

public int myFunction(string parameterOne, string parameterTwo) {
  // Can't work without a value
  if (string.IsNullOrEmpty(parameterOne)) {
    throw new ArgumentNullException("parameterOne");
  } 
  if (string.IsNullOrEmpty(parameterTwo)) {
    throw new ArgumentNullException("parameterTwo");
  }

  // ...      
  // Do some work
  // ...

  return value;
}
32
rjzii

我希望早日返回。

如果您有一个入口点和一个出口点,那么您始终必须一直在脑海中一直跟踪整个代码,直到出口点为止(您永远不知道其他代码是否会对结果产生其他影响,所以您必须跟踪直到存在)。您不必执行哪个分支即可确定最终结果。这很难遵循。

有了一个条目并存在多个条目,您将在获得结果时返回,而不必费心追踪所有结果,以至于没有人对它做任何其他事情(因为自从您返回以来,将不再有其他任何事情)。就像将方法主体拆分为更多的步骤一样,每个步骤都有可能返回结果或让下一步尝试运气。

24
user7197

在必须手动清理的C编程中,对于单点返回有很多说法。即使现在不需要清理某些内容,也可以有人编辑您的函数,分配一些内容并需要在返回之前进行清理。如果发生这种情况,遍历所有return语句将是一场噩梦。

在C++编程中,您有析构函数,甚至还有作用域退出防护。所有这些都需要放在这里,以确保代码首先是异常安全的,因此可以很好地防止代码过早退出,因此这样做没有逻辑上的弊端,而纯粹是样式问题。

我对Java不够了解,是否会调用“最终”块代码以及终结器是否可以处理需要确保某些事情发生的情况。

C#我当然无法回答。

D语言为您提供了适当的内置范围退出保护,因此为提前退出做好了充分的准备,因此除样式外不应出现其他问题。

当然,函数起初应该没有那么长,如果您有一个巨大的switch语句,您的代码也可能会被分解。

13
CashCow

我都用

如果DoSomething是3-5行代码,则使用第一种格式化方法,代码看起来就很漂亮。

但是,如果它的行数多于该行数,那么我更喜欢第二种格式。当左括号和右括号不在同一屏幕上时,我不喜欢。

9
azheglov

争取双赢的早期回报。它们看起来很丑陋,但是比大型if包装器丑陋得多,尤其是在要检查多个条件的情况下。

9
STW

一次进入单次退出的一个经典原因是,否则,形式语义就变得很难看(否则,GOTO被认为是有害的)。

换句话说,如果只有1个返回,则更容易推断何时软件将退出例程。这也是反对例外的理由。

通常,我会尽量减少提前归还的方法。

8
Paul Nathan

就个人而言,我更喜欢在开始时进行通过/失败条件检查。这样一来,我就可以将大多数最常见的故障归类到该函数的顶部,并遵循其余的逻辑。

7
nathan

这取决于。

如果有一些明显的死角条件需要立即检查,则提前返回,这将使其余函数的运行毫无意义。*

如果函数更复杂并且否则可能具有多个出口点,则设置Retval +单返回(可读性问题)。

* 这通常可以指示设计问题。如果您发现很多方法在运行其余代码之前需要检查某些外部/参数状态等,则可能是调用者应处理的事情

6
Bobby Tables

使用If

在Don Knuth关于GOTO的书中,我读到他在if语句中给出始终使最可能的条件始终存在的理由。假设这仍然是一个合理的想法(而不是出于对时代速度的单纯考虑)。我要说早期返回不是良好的编程习惯,尤其是考虑到这样的事实,即它们经常用于错误处理,除非您的代码更有可能失败而不是失败:-)

如果遵循上述建议,则需要将该返回值放在函数的底部,然后您甚至不愿在该函数处将其称为返回值,只需设置错误代码并返回两行即可。从而实现1入口1出口的理想状态。

特定于Delphi ...

我认为这对Delphi程序员来说是一种很好的编程习惯,尽管我没有任何证据。在D2009之前,我们没有原子方法来返回值,我们有exit;result := foo;,否则我们可以抛出异常。

如果您不得不替代

if (true) {
 return foo;
} 

for

if true then 
begin
  result := foo; 
  exit; 
end;

您可能会讨厌在每个功能的顶部看到它,并且更喜欢

if false then 
begin
  result := bar;

   ... 
end
else
   result := foo;

并完全避免exit

3
Peter Turner

我同意以下声明:

我个人是警卫条款的拥护者(第二个示例),因为它减少了函数的缩进。有些人不喜欢它们,因为它会导致该函数产生多个返回点,但我认为使用它们会更清楚。

取自 stackoverflow中的此问题

2
Toto

我更愿意写:

if(someCondition)
{
    SomeFunction();
}
1
Josh K

像您一样,我通常会写第一个,但更喜欢最后一个。如果我有很多嵌套的支票,我通常会重构为第二种方法。

我不喜欢如何将错误处理从检查中移开。

if not error A
  if not error B
    if not error C
      // do something
    else handle error C
  else handle error B
else handle error A

我更喜欢这样:

if error A
  handle error A; return
if error B
  handle error B; return
if error C
  handle error C; return

// do something
1
jolt

这些天来,我几乎完全将早期回报用于极端情况。我写这个

self = [super init];

if (self != nil)
{
    // your code here
}

return self;

self = [super init];
if (!self)
    return;

// your code here

return self;

但这并不重要。如果您的函数中有多个嵌套级别,则需要将其嵌套。

1
Dan Rosenstark

顶部的条件称为“前提条件”。通过放置if(!precond) return;,您可以直观地列出所有先决条件。

使用较大的“ if-else”块可能会增加缩进开销(我忘记了有关三级缩进的引用)。

0
Ming-Tang