我经常用两种格式编写这种函数,我想知道一种格式是否比另一种格式更受青睐,为什么?
class =“ lang-java prettyprint-override”> public void SomeFunction(bool someCondition) { if (someCondition) { // Do Something } }



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

    // Do Something
}
  

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

评论

我这次讨论有点晚了,所以我不会回答。我也在两年前考虑了这一点:lecterror.com/articles/view/code-formatting-and-readability我发现第二个更易于阅读,修改,维护和调试。但也许那只是我:)

相关:“仅返回”的概念从何而来?

现在,这个问题是基于观点的问题的一个很好的例子

那么如果在一个或另一个方向上没有绝对证据怎么办?如果在一个方向和另一个方向上提供了足够的论据,并且答案正确,则可以进行投票-这将非常有用。我发现像这样的封闭问题对本网站的价值有害。

我喜欢基于意见的问题和答案。他们告诉我大多数人更喜欢什么,这使我可以编写代码供他人阅读。

#1 楼

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

评论


Smalltalk称这些为“保护条款”。至少这就是肯特·贝克(Kent Beck)在Smalltalk最佳实践模式中所说的;我不知道这是否是常见的说法。

–坦率的剪毛
2010年11月11日19:37

提早离开可以让您摆脱有限的思维障碍。 :)

– Joren
2010年11月12日在7:59

我以前曾听说过这种被称为“弹跳器模式”(Bouncer Pattern)的方法-在坏情况出现之前就将其清除。

– RevBingo
10 Nov 12 '21:02

我什至会说这绝对是唯一正确的选择。

–奥利弗·韦勒
2010年12月6日,0:34

另外,如果一开始就没有边框,您就不会增加缩进。

–doppelgreener
2011-02-18 13:31

#2 楼

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



 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;
}
 


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

评论


单个出口点的优点是...有一个出口点!在您的示例中,有几点可能会返回。使用更复杂的功能,当返回值的格式更改时,可能会变成寻线点。当然,有时候强制单个出口点没有意义。

–JohnL
2010年11月11日在21:17

@JohnL大函数是问题,不是多个出口点。当然,除非您在额外的函数调用会极大地减慢代码速度的环境中工作,否则...

–丹·罗森斯塔克(Dan Rosenstark)
2010年11月11日在21:23

@Yar:是的,但重点是。我不会尝试转换任何人,而只是指出了尽可能减少出口点的优势(无论如何,Jason的例子有点像个稻草人)。只是在下一次您要弄清楚为什么SomeFunction有时返回奇数值的时候,也许您希望您可以在返回之前添加一个日志记录调用。如果只有一个虫子,调试起来会容易得多! :)

–JohnL
2010年11月11日23:11

@Jason Viers好像是单个返回值;帮助!?!然后,您必须搜寻半打值= ...,其缺点是您无法确定在此赋值和最终收益之间值不会改变。至少立即返回显然是什么也不会改变结果。

– Sjoerd
2011-02-18 17:26

@Sjoed:我在这里第二个Sjoerd。如果要记录结果,则可以登录到呼叫者站点。如果您想知道原因,则必须在每个出口点/分配处登录,因此这两个方面都相同。

– Matthieu M.
2011-2-18在18:46



#3 楼

这取决于-通常,我不会不遗余力地尝试移动一堆代码以尽早脱离该功能-编译器通常会为我解决这个问题。话虽如此,但是如果我需要顶部的一些基本参数,否则无法继续进行,那么我将尽早进行突破。同样,如果条件在函数中生成了一个巨大的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;
}
 


评论


如果最终结果是可维护的,那么谁在乎选择哪种样式呢?

– Jeff Siver
2010年11月11日19:32

@Jeff Siver-因此,为什么这总是一个“圣战”风格的问题,最终归结为个人喜好和内部风格指南所说的一切。

–rjzii
2010年11月11日,19:33

这里的主要收获是,他抛出异常而不是早日返回。返回值不应重新用于有效性检查。如果您有各种条件,并且想让使用该方法的代码知道失败的原因怎么办?突然,您可能会从一个方法返回实际的业务数据,什么也没有(空结果)或许多不同的字符串,代码,数字等。仅描述其失败原因。不用了,谢谢。

– DanMan
2014年5月24日10:51



我的编译器建议的圈复杂度如何?如果可以,最好不要嵌套代码吗?

– Alex Gordon
16-4-1的3:19

#4 楼

我更喜欢早期返回。下面的其他代码对结果做了其他处理,因此您必须对其进行跟踪直到存在为止)。您不必执行哪个分支即可确定最终结果。这很难遵循。

只有一个条目并且存在多个条目,您在有结果时会返回,而不必费心跟踪所有结果,因为没人会对其进行任何操作(因为自您返回以来,将不会有其他任何事情)。就像将方法主体拆分为更多的步骤一样,每个步骤都有可能返回结果或让下一步尝试运气。

#5 楼

在必须手动清理的C编程中,对于单点返回有很多说法。即使现在不需要清理某些内容,也可以有人编辑您的函数,分配一些内容并需要在返回之前进行清理。如果发生这种情况,那么查看所有return语句将是一场噩梦。所有这些都需要放在这里,以确保代码从一开始就具有异常安全性,因此可以很好地防止代码过早退出,因此这样做没有逻辑上的弊端,而且纯粹是样式问题。我对Java不够了解,是否会调用“最终”块代码以及终结器是否可以处理需要确保某些事情发生的情况。

C#我当然无法回答。 />
D语言为您提供了适当的内置范围退出保护,因此为提前退出做好了充分的准备,因此除样式外不应出现其他问题。当然,开始的时间不会太长,如果您有一个巨大的switch语句,您的代码也可能会被分解。

评论


C ++允许一种称为返回值优化的方法,它允许编译器实质上省略通常在您返回值时发生的复制操作。但是,在各种情况下都很难做到,多个返回值就是其中一种情况。换句话说,在C ++中使用多个返回值实际上会使代码变慢。这肯定在MSC上成立,并且考虑到用多个可能的返回值实现RVO是非常棘手的(如果不是不可能的话),这在所有编译器中都可能是一个问题。

–Eamon Nerbonne
2014年5月20日14:27



在C语言中,只需使用goto和可能的两点返回。示例(注释中无法进行代码格式化):foo(){init();如果(不好)出错了;酒吧();如果(不好)出错了; baz();返回0;错误:cleanup();返回1; }

– mirabilos
2014年5月24日12:20

我不喜欢goto,而是选择“提取方法”。当您认为需要实现返回值变量或goto时,为了确保始终调用清理代码,这是一个气味,您需要将其分解为多个函数。这使您可以使用Guard子句和其他早期返回技术来简化复杂的条件代码,同时仍确保清理代码始终运行。

–布兰登·怀特
2015年9月17日14:48在

“有人可能会编辑您的功能”-这对于决策来说是一个荒谬的解释。将来任何人都可以做任何事情。这并不意味着您今天应该做一些特定的事情来防止某人将来破坏事情。

– Victor Yarema
18-10-23在18:42

称为使您的代码可维护。在现实世界中,代码是出于商业目的而编写的,有时开发人员稍后需要对其进行更改。当时我写了这个答案,尽管我已经修补了CURL以引入缓存,并回想起了通俗的说,这确实需要用现代C ++重写

– CashCow
18-10-30在14:55

#6 楼

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

#7 楼



如果DoSomething是3-5行代码,那么使用第一种格式化方法,代码看起来就很漂亮。 ,那么我更喜欢第二种格式。我不喜欢开括号和闭括号不在同一个屏幕上。

评论


美丽没办法!压痕太多!

– JimmyKane
16年4月24日在19:40

@JimmyKane在3-5行中只能出现这么多的缩进,特别是当您每级需要2(?)行时,其余的行会缩进:一个用于控制结构和块的开始,一个用于块的结束。

–重复数据删除器
18-10-2在2:47



#8 楼

一次进入单次退出的一个经典原因是,否则,形式语义就变得丑陋不堪(同样,GOTO被认为是有害的)。

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

通常,我将提前归还方法减至最少。

评论


但是对于正式的分析工具,您可以将外部函数与该工具所需的语义进行综合,并使代码易于阅读。

–蒂姆·威利斯克罗夫特(Tim Williscroft)
2010年11月11日在21:48

@蒂姆这取决于您要进行多少次餐饮以及您要分析的内容。如果编码人员对事物保持理智,我发现SESE相当可读。

– Paul Nathan
2010年11月11日在22:09

我对分析的态度受Self项目的优化影响。 99%的动态方法调用可以静态解析和消除。这样您就可以分析一些漂亮的直线代码。作为一个工作的程序员,我可以可靠地假设我将要处理的大多数代码是由普通程序员编写的。因此他们不会编写非常好的代码。

–蒂姆·威利斯克罗夫特(Tim Williscroft)
2010年11月11日在22:31

@蒂姆:足够公平。尽管我确实不得不指出静态语言仍然发挥着重要作用。

– Paul Nathan
2010年11月11日22:36

面对异常没有理由。这就是为什么单点退出在C语言中表现出色,却在C ++中不会给您带来任何好处的原因。

–peterchen
2010年11月11日23:15

#9 楼

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

#10 楼

这要视情况而定。

如果有任何明显的死角条件需要立即检查,这将使其余函数毫无意义地运行,则提前返回。如果函数更复杂并且否则可能具有多个退出点(可读性问题),则返回。

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

评论


当我编写可以共享的代码时,我的口头禅是“不承担任何责任。不信任任何人”。您应该始终验证输入以及您依赖的任何外部状态。最好抛出一个异常而不是破坏某些东西,因为有人给了您错误的数据。

– TMN
2010年11月11日在20:59

@TMN:好点。

– Bobby Tables
2010年11月11日23:41

这里的关键是在OO中抛出异常,而不是返回异常。多次返回可能是不好的,多次抛出异常并不一定是代码的味道。

– Michael K
2010年11月12日13:59

@MichaelK:如果无法满足后置条件,则方法应为异常。在某些情况下,方法应该提早退出,因为即使在功能开始之前也已经达到了后置条件。例如,如果调用“设置控件标签”方法以将控件的标签更改为Fred,则窗口的标签已经是Fred,并且将控件的名称设置为当前状态将强制重绘(这在某些情况下可能很有用) ,这很烦人),如果旧名称和新名称匹配,则尽早使用set-name方法是完全合理的。

–超级猫
15年1月13日在18:15

#11 楼

在Don Knuth关于GOTO的书中使用If

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

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

Delphi特有的...

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

如果必须替换

 if (true) {
 return foo;
} 
 


代替



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


您可能会厌倦在在您的每个功能中都优先使用,并且更喜欢


评论


对于较新的Delphis,可以将其缩写为true,然后退出(foo);我经常使用这种技术,首先将结果分别初始化为nil或FALSE,然后检查所有错误情况,然后退出。如果满足。然后(通常)在方法末尾设置成功案例的结果。

– JensG
2014年5月21日在8:13



是的,我喜欢这个新功能,尽管它看起来像是使Java程序员安心的糖果,接下来您知道他们将让我们在过程中定义变量。

– Peter Turner
2014年5月21日14:10

和C#程序员。是的,说实话,我会发现确实有用,因为它减少了声明和使用之间的界限(IIRC甚至有一些度量标准,忘记了名称)。

– JensG
2014年5月21日20:00



#12 楼

我同意以下声明:


我个人是保护子句的粉丝(第二个示例),因为它减少了函数的
缩进。有些人不喜欢它们,因为它会导致
函数中有多个返回点,但我认为与他们在一起更清晰。


来自stackoverflow中的这个问题。

评论


+1对于保护条款。我也更喜欢正面的守卫,例如:if(condition = false)return而不是if(!condition)return

– JustinC
2011-2-18在16:28



#13 楼

这些天来,我几乎全部使用早期回报,这是非常极端的。我写这个



 self = [super init];

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

return self;
 


作为

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

// your code here

return self;
 


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

评论


我同意。缩进是造成阅读困难的原因。压痕越少越好。您的简单示例具有相同的缩进级别,但是第一个示例肯定会增长为需要更多脑力的更多缩进。

–user441521
17年5月4日在17:35

#14 楼

我更愿意这样写:

 if(someCondition)
{
    SomeFunction();
}
 


评论


ew那是预验证吗?还是在验证专用的方法DoSomeFunctionIfSomeCondition中?

– STW
2010年11月11日19:27

这如何使您的关注点分开?

–justkt
2010年11月11日20:00

您通过将函数的实现(其依赖关系逻辑)置于外部来违反封装。

– TMN
2010年11月11日20:45

如果此方法在公共方法中运行,并且SomeFunction()是同一类中的私有方法,则可能没问题。但是,如果其他人正在调用SomeFunction(),则必须在此处重复检查。我发现最好使每种方法都能完成其工作所需的一切,没有其他人应该知道这一点。

– Per Wiklander
2010年11月11日23:34

这绝对是罗伯特·C·马丁(Robert C. Martin)在“清洁代码”中提出的风格。函数只能做一件事。但是,肯特·贝克(Kent Beck)建议在“实施模式”中,在OP中提出的两个选择中,第二个更好。

–斯科特·惠特洛克
2010年11月13日在1:20

#15 楼

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

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



 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
 


我更喜欢这样:

#16 楼

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

评论


什么?您无法使用Java提前归还吗? C#,VB(.NET和6),以及显然的Java(我以为是,但是由于15年来我一直没有使用该语言,因此不得不进行查找)都允许早期返回。因此,不要指责“强类型语言”没有该功能。 stackoverflow.com/questions/884429/…

–ps2goat
16年8月2日在21:49

#17 楼

我宁愿保持if语句较小。

因此,请在以下选项之间进行选择: if condition: line1 line2 ... line-n




 if not condition: return

line1
line2
 ...
line-n
 


我' d选择您所说的“早期回报”。

介意您,我不在乎早期的回报或其他任何事情,我只是想简化代码,缩短if语句的主体等。

而且一劳永逸,请不惜一切代价避免使用它们。