例如,当我在我的代码上运行ReSharper时:
if (some condition)
{
Some code...
}
ReSharper给了我上面的警告(反转“if”语句以减少嵌套),并建议以下更正:
if (!some condition) return;
Some code...
我想知道为什么这样更好。我一直认为在方法中间使用“return”是有问题的,有点像“goto”。
例如,当我在我的代码上运行ReSharper时:
if (some condition)
{
Some code...
}
ReSharper给了我上面的警告(反转“if”语句以减少嵌套),并建议以下更正:
if (!some condition) return;
Some code...
我想知道为什么这样更好。我一直认为在方法中间使用“return”是有问题的,有点像“goto”。
当前回答
它不仅美观,而且还减少了方法内部的最大嵌套级别。这通常被认为是一个优点,因为它使方法更容易理解(事实上,许多静态分析工具提供了一种度量方法,作为代码质量的指标之一)。
另一方面,它还使您的方法具有多个退出点,另一组人认为这是不可取的。
就我个人而言,我同意ReSharper和第一组(在有例外的语言中,我发现讨论“多个退出点”是愚蠢的;几乎任何东西都可能抛出,所以在所有方法中都有许多潜在的退出点)。
关于性能:每种语言的两个版本都应该是等效的(如果不是在IL级别,那么肯定是在代码抖动结束后)。从理论上讲,这取决于编译器,但实际上,今天任何广泛使用的编译器都能够处理比这更高级的代码优化情况。
其他回答
在性能方面,这两种方法之间没有明显的区别。
但是编码不仅仅关乎性能。清晰性和可维护性也非常重要。而且,在这种情况下,它不会影响性能,这是唯一重要的事情。
关于哪种方法更可取,存在着相互竞争的思想流派。
一种观点是其他人已经提到的观点:第二种方法减少了嵌套级别,从而提高了代码的清晰度。这在命令式风格中是很自然的:当你没有什么事情要做的时候,你不妨早点回来。
从函数式风格的角度来看,另一种观点认为一个方法应该只有一个出口点。函数式语言中的一切都是表达式。所以if语句必须总是有一个else子句。否则if表达式并不总是有值。所以在函数式风格中,第一种方法更自然。
这里已经有很多有见地的答案,但是,我仍然想要指向一个稍微不同的情况:而不是前置条件,它实际上应该放在函数的顶部,考虑一步一步的初始化,在那里你必须检查每一步是否成功,然后继续下一步。在这种情况下,您不能检查顶部的所有内容。
当我使用Steinberg的ASIOSDK编写ASIO主机应用程序时,我发现我的代码真的难以阅读,因为我遵循了嵌套范式。它有8层深,我看不出有什么设计缺陷,正如Andrew Bullock上面提到的那样。当然,我可以将一些内部代码打包到另一个函数中,然后在那里嵌套剩余的级别以使其更具可读性,但对我来说这似乎相当随机。
通过用保护子句替换嵌套,我甚至发现了自己的一个误解,即一部分清理代码应该在函数的早期出现,而不是在函数的末尾。对于嵌套分支,我从来没有看到过,你甚至可以说它们导致了我的误解。
所以这可能是另一种情况,反向if可以有助于更清晰的代码。
方法中间的返回并不一定是坏的。如果能使代码的意图更清晰,那么立即返回可能会更好。例如:
double getPayAmount() {
double result;
if (_isDead) result = deadAmount();
else {
if (_isSeparated) result = separatedAmount();
else {
if (_isRetired) result = retiredAmount();
else result = normalPayAmount();
};
}
return result;
};
在这种情况下,如果_isDead为真,我们可以立即退出该方法。也许这样结构会更好:
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};
我从重构目录中挑选了这段代码。这种特殊的重构被称为:用保护子句替换嵌套条件句。
它不仅影响美观,而且还阻止了代码嵌套。
它实际上也可以作为确保数据有效的前提条件。
保护子句或先决条件(正如您可能看到的那样)检查是否满足某个条件,然后中断程序的流程。它们适用于只对if语句的一个结果感兴趣的地方。所以不要说:
if (something) {
// a lot of indented code
}
反转条件,如果反转条件满足,则中断
if (!something) return false; // or another value to show your other code the function did not execute
// all the code from before, save a lot of tabs
返回远没有去到那么脏。它允许您传递一个值来显示函数无法运行的其余代码。
你将看到在嵌套条件下应用这个的最佳示例:
if (something) {
do-something();
if (something-else) {
do-another-thing();
} else {
do-something-else();
}
}
vs:
if (!something) return;
do-something();
if (!something-else) return do-something-else();
do-another-thing();
你会发现很少有人认为第一种说法更清晰,当然,这完全是主观的。有些程序员喜欢通过缩进来知道某些东西在什么条件下运行,而我更愿意保持方法流是线性的。
我不是说precons会改变你的生活或让你上床,但你可能会发现你的代码更容易阅读。