假设有以下代码:
void DoThis()
{
if (!isValid) return;
DoThat();
}
void DoThat() {
Console.WriteLine("DoThat()");
}
在void方法中使用return是否可行?这样做是否会对性能产生影响?还是最好像这样编写代码:
void DoThis()
{
if (isValid)
{
DoThat();
}
}
假设有以下代码:
void DoThis()
{
if (!isValid) return;
DoThat();
}
void DoThat() {
Console.WriteLine("DoThat()");
}
在void方法中使用return是否可行?这样做是否会对性能产生影响?还是最好像这样编写代码:
void DoThis()
{
if (isValid)
{
DoThat();
}
}
使用守卫(而非嵌套代码)的另一个极好的原因在于:如果另一个程序员向您的函数添加代码,则他们将在更安全的环境中工作。
考虑以下情况:
void MyFunc(object obj)
{
if (obj != null)
{
obj.DoSomething();
}
}
对比:
void MyFunc(object obj)
{
if (obj == null)
return;
obj.DoSomething();
}
现在,想象另一个程序员添加了这行代码:obj.DoSomethingElse();
void MyFunc(object obj)
{
if (obj != null)
{
obj.DoSomething();
}
obj.DoSomethingElse();
}
void MyFunc(object obj)
{
if (obj == null)
return;
obj.DoSomething();
obj.DoSomethingElse();
}
显然这只是一个简单的例子,但在第一个(嵌套代码)示例中,程序员已经在程序中添加了一次崩溃。在第二个示例(使用守卫早期退出),一旦通过了守卫,您的代码就可以安全地避免意外使用空引用。
当然,优秀的程序员不会经常犯这样的错误。但是预防胜于治疗——我们可以通过编写消除此潜在错误源的代码来消除这个问题。嵌套会增加复杂度,因此最佳实践建议重构代码以减少嵌套。
不好的实践?绝对不是这样。事实上,如果验证失败,则尽早从方法中返回来处理验证总是更好的做法。 否则,将导致大量嵌套的if和else语句。提前终止有助于提高代码可读性。
还要查看类似问题的答案:我应该使用return/continue语句还是if-else语句?
这并不是一个坏的做法(因为之前已经提到了所有原因)。然而,如果一个方法中有很多返回值,那么它应该被拆分成更小的逻辑方法。
void DoThis()
{
if (guard1) return;
if (guard2) return;
...
if (guardN) return;
DoThat();
}
我认为这比下面的更易读:
void DoThis()
{
if (guard1 && guard2 && guard3)
{
DoThat();
}
}
没有性能惩罚,但第二段代码更易读,因此更容易维护。
这是完全可以的,没有“性能惩罚”,但永远不要写一个没有括号的“if”语句。
始终如此。
if( foo ){
return;
}
这样代码更易读,你也不会错误地假设某些代码部分在该语句内。
{
前按下回车键。这将使你的 {
与 }
在同一列中对齐,大大提高了可读性(更容易找到相应的开/闭括号)。 - Imagistif
语句需要右括号将变得容易,因此将 if
语句控制单个语句将是安全的。将左括号推回到 if
所在行会为每个多语句 if
节省一行垂直空间,但这将需要使用一个否则不必要的右括号行。 - supercat我要在这个问题上不同意你们所有的年轻人。
在方法中使用return,无论是void还是其他类型,都是非常糟糕的做法,这是由已故的Edsger W. Dijkstra在近40年前清晰地阐述的原因,从著名的“GOTO语句有害”开始,一直到Dahl、Dijkstra和Hoare的“结构化编程”。
基本规则是每个控制结构和每个模块都应该有一个入口和一个出口。在模块中显式使用return会打破这个规则,并且使得程序状态更难以理解,进而使得判断程序是否正确变得更加困难(这比“是否能正常工作”更强的属性)。
“GOTO语句有害”和“结构化编程”开启了20世纪70年代的“结构化编程”革命。这两篇文章是我们今天拥有if-then-else、while-do和其他显式控制结构的原因,也是为什么高级语言中的GOTO语句在濒危物种名单上的原因。(我的个人观点是它们需要被列入灭绝物种名单。)
值得注意的是,消息流调制器是第一款军用软件,它在第一次尝试通过验收测试时没有出现任何偏差、豁免或“但是”的措辞,而且它是用一种甚至没有GOTO语句的语言编写的。在使用守卫时,请确保遵循某些准则,以免混淆读者。
示例
// guards point you to the core intent
void Remove(RayCastResult rayHit){
if(rayHit== RayCastResult.Empty)
return
;
rayHit.Collider.Parent.Remove();
}
// no guards needed: function split into multiple cases
int WonOrLostMoney(int flaw)=>
flaw==0 ? 100 :
flaw<10 ? 30 :
flaw<20 ? 0 :
-20
;