是否有充分的理由说明在函数中只有一个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.
其他回答
永远不要在方法中使用return语句。
我知道我会因此受到指责,但我是认真的。
返回语句基本上是过程式编程时代遗留下来的。它们是goto的一种形式,以及break、continue、if、switch/case、while、for、yield和其他一些语句以及大多数现代编程语言中的等价语句。
返回语句有效地“GOTO”函数被调用的点,并在该范围内分配一个变量。
返回语句就是我所说的“方便的噩梦”。它们似乎能快速完成任务,但却会导致大量的维护问题。
Return语句与封装语句截然相反
这是面向对象编程中最重要和最基本的概念。这是面向对象的存在理由。
当你从一个方法返回任何东西时,你基本上是在从对象中“泄露”状态信息。不管你的状态是否改变了,也不管这个信息是否来自其他对象——对调用者来说没有区别。这样做的目的是允许对象的行为在对象分解封装之外。它允许调用者开始以导致脆弱设计的方式操纵对象。
LoD是你的朋友
我建议任何开发人员在c2.com或维基百科上阅读有关得墨忒耳定律(LoD)的内容。LoD是一种设计理念,在字面意义上被用于真正的“关键任务”软件约束的地方,比如喷气推进实验室。它已被证明可以减少代码中的错误数量并提高灵活性。
关于遛狗有一个很好的类比。当你遛狗的时候,你不需要身体上抓住它的腿并移动它们,这样狗就会走。你命令狗走路,它会照顾好自己的腿。在这个类比中,return语句相当于狗让你抓住它的腿。
只和你最亲近的朋友聊天:
你所在函数的参数, 你自己的属性, 在函数中创建的任何对象
您将注意到,这些都不需要return语句。您可能认为构造函数是一个返回函数,这样您就得到了一些东西。实际上返回的是内存分配器。构造函数只是设置内存中的内容。只要新对象的封装是OK的,这就没问题,因为,当你创建它时,你对它有完全的控制权——没有人可以破坏它。
Accessing attributes of other objects is right out. Getters are out (but you knew they were bad already, right?). Setters are OK, but it is better to use constructors. Inheritance is bad - when you inherit from another class, any changes in that class can and probably will break you. Type sniffing is bad (Yes - LoD implies that Java/C++ style type based dispatch is incorrect - asking about type, even implicitly, is breaking encapsulation. Type is an implicit attribute of an object. Interfaces are The Right Thing).
So why is this all a problem? Well, unless your universe is very different from mine, you spend a lot of time debugging code. You aren't writing code that you plan never to reuse. Your software requirements are changing, and that causes internal API/interface changes. Every time you have used a return statement you have introduced a very tricky dependency - methods returning anything are required to know about how whatever they return is going to be used - that is each and every case! As soon as the interface changes, on one end or the other, everything can break, and you are faced with a lengthy and tedious bug hunt.
它们实际上是代码中的恶性肿瘤,因为一旦您开始使用它们,它们就会促进在其他地方进一步使用(这就是为什么您经常可以在对象系统中找到返回的方法链)。
那么另一种选择是什么?
告诉,不要问。
使用OOP——目标是告诉其他对象要做什么,并让它们来处理。所以你必须忘记做事的程序方法。其实很简单——只要不写return语句。做同样的事情有更好的方法:
return概念没有错,但是return语句有很大的缺陷。
如果你真的需要回复,那就回电话。甚至可以传入要填写的数据结构。通过这种方式,您可以保持接口的干净和开放,并且您的整个系统不那么脆弱,适应性更强。它不会减慢你的系统速度,事实上它可以加快系统速度,就像尾部调用优化一样——除了在这种情况下,没有尾部调用,所以你甚至不必浪费时间用返回值来操作堆栈。
如果遵循这些参数,就会发现实际上根本不需要return语句。
如果您遵循这些实践,我保证您很快就会发现您花在寻找错误上的时间要少得多,适应需求变化的速度要快得多,理解您自己的代码时遇到的问题也要少得多。
我倾向于单一退出,除非事情真的变得复杂。我发现在某些情况下,多个存在点可以掩盖其他更重要的设计问题:
public void DoStuff(Foo foo)
{
if (foo == null) return;
}
在看到这段代码时,我马上会问:
'foo'是否为空? 如果是这样,有多少客户端'DoStuff'曾经调用一个空'foo'函数?
根据这些问题的答案,可能是这样
这种检查毫无意义,因为它从来都不是真的。它应该是一个断言) 这种检查很少是正确的,所以最好改变那些特定的调用函数,因为它们可能应该采取一些其他的操作。
在上述两种情况下,代码可能都可以用断言重做,以确保'foo'永远不为空,并更改相关的调用者。
还有另外两个原因(我认为是针对c++代码的),多重存在实际上会产生负面影响。它们是代码大小和编译器优化。
在函数出口作用域中的非pod c++对象将调用其析构函数。如果有几个return语句,那么作用域中可能有不同的对象,因此要调用的析构函数列表也会不同。因此,编译器需要为每个return语句生成代码:
void foo (int i, int j) {
A a;
if (i > 0) {
B b;
return ; // Call dtor for 'b' followed by 'a'
}
if (i == j) {
C c;
B b;
return ; // Call dtor for 'b', 'c' and then 'a'
}
return 'a' // Call dtor for 'a'
}
如果代码大小是一个问题,那么这可能是值得避免的。
另一个问题涉及到“命名返回值优化”(又名复制省略,ISO c++ '03 12.8/15)。c++允许实现在可以的情况下跳过调用复制构造函数:
A foo () {
A a1;
// do something
return a1;
}
void bar () {
A a2 ( foo() );
}
就像代码一样,对象'a1'是在'foo'中构造的,然后它的复制构造将被调用来构造'a2'。然而,复制省略允许编译器在堆栈上与'a2'相同的位置构造'a1'。因此,当函数返回时,不需要“复制”对象。
多个出口点会使编译器的检测工作复杂化,至少对于相对较新的vc++版本,优化不会发生在函数体有多个返回的地方。有关详细信息,请参阅Visual c++ 2005中的命名返回值优化。
这可能是一个不寻常的观点,但我认为任何相信多个return语句是有利的人都从未在只支持4个硬件断点的微处理器上使用过调试器。:-)
虽然“箭头代码”的问题是完全正确的,但当使用多个return语句时,一个问题似乎消失了,那就是在使用调试器的情况下。您没有一个方便的万能位置来放置断点,以保证您将看到退出,从而看到返回条件。
拥有多个出口点本质上与使用GOTO是一样的。这是不是件坏事取决于你对迅猛龙的看法。
函数中return语句越多,该方法的复杂性就越高。如果您发现自己想知道是否有太多的return语句,那么您可能需要问问自己,该函数中是否有太多的代码行。
但是,不是,一个/多个返回语句并没有错。在某些语言中,这是一种比其他语言(C)更好的实践(c++)。