想象一下以下代码:
void DoThis()
{
if (!isValid) return;
DoThat();
}
void DoThat() {
Console.WriteLine("DoThat()");
}
在void方法中使用return是否可以?它有任何性能损失吗?或者写一个这样的代码会更好:
void DoThis()
{
if (isValid)
{
DoThat();
}
}
答案 0 :(得分:161)
void方法中的返回并不错,这是invert if
statements to reduce nesting的常见做法。
在方法上嵌套较少可以提高代码的可读性和可维护性。
实际上如果你有一个没有任何return语句的void方法,编译器总会在它的末尾生成一个ret instruction。
答案 1 :(得分:27)
使用防护(与嵌套代码相对)还有另一个很好的理由:如果另一个程序员将代码添加到您的函数中,它们就会在更安全的环境中工作。
考虑:
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();
}
显然这是一个简单的案例,但程序员在第一个(嵌套代码)实例中为程序添加了一个崩溃。在第二个例子中(早期退出守卫),一旦你越过守卫,你的代码就可以安全地无意中使用空引用。
当然,一个优秀的程序员不会犯这样的错误(经常)。但预防胜于治疗 - 我们可以用一种完全消除这种潜在错误来源的方式编写代码。嵌套增加了复杂性,因此最佳实践建议重构代码以减少嵌套。
答案 2 :(得分:17)
糟糕的做法???没门。事实上,如果验证失败,最好通过最早从方法返回来处理验证。否则会导致大量的嵌套ifs&别人的。提前终止可提高代码的可读性。
同时查看类似问题的回复:Should I use return/continue statement instead of if-else?
答案 3 :(得分:6)
这是不错的做法(由于所有原因已经说明)。但是,您在方法中获得的回报越多,就越有可能将其拆分为更小的逻辑方法。
答案 4 :(得分:4)
第一个例子是使用guard语句。来自Wikipedia:
在计算机编程中,守卫是一个 必须评估的布尔表达式 如果程序执行到,则返回true 继续在有问题的分支。
我认为在方法的顶部有一堆守卫是一种完全可以理解的编程方式。它基本上是说“如果这些方法中的任何一个都是真的,不要执行此方法”。
所以一般情况下它会这样:
void DoThis()
{
if (guard1) return;
if (guard2) return;
...
if (guardN) return;
DoThat();
}
我认为那更具可读性:
void DoThis()
{
if (guard1 && guard2 && guard3)
{
DoThat();
}
}
答案 5 :(得分:3)
没有性能损失,但第二段代码更易读,因此更容易维护。
答案 6 :(得分:2)
这完全没问题,也没有“性能损失”,但从来没有写过没有括号的'if'语句。
始终
if( foo ){
return;
}
它更具可读性;并且你永远不会意外地假设代码的某些部分在它们不在的情况下。
答案 7 :(得分:1)
在这种情况下,你的第二个例子是更好的代码,但这与从void函数返回无关,这只是因为第二个代码更直接。但是从void函数返回是完全没问题的。
答案 8 :(得分:0)
我将不同意你们所有年轻的鞭挞者。
在方法中间使用返回,无效或其他方式,是非常糟糕的做法,因为近四十年前由已故的Edsger W. Dijkstra清楚地阐明的原因,从众所周知的“GOTO”开始声明被Dahl,Dijkstra和Hoare认为是“有害的”,并继续进行“结构化编程”。
基本规则是每个控制结构和每个模块应该只有一个入口和一个出口。模块中间的显式返回打破了该规则,并且使得更难以推断程序的状态,这反过来使得更难以说程序是否正确(这是一个更强大的属性)而不是“它是否起作用”)。
“被认为有害的GOTO声明”和“结构化编程”开启了20世纪70年代的“结构化编程”革命。这两个部分是我们今天使用if-then-else,while-do和其他显式控制结构的原因,以及为什么高级语言中的GOTO语句位于Endangered Species列表中。 (我个人认为他们需要在灭绝物种清单上。)
值得注意的是,消息流调制器是EVER第一次通过验收测试的第一块军用软件,没有任何偏差,弃权或“是的,但是”措辞,是用一种语言编写的甚至没有GOTO声明。
值得一提的是,Nicklaus Wirth改变了Oberon-07中RETURN语句的语义,这是Oberon编程语言的最新版本,使其成为类型化程序(即函数)声明的尾随部分,而不是函数体中的可执行语句。他对变化的解释表明他之所以这么做是因为前一种形式 WAS 违反了结构化程序设计的一次退出原则。
答案 9 :(得分:0)
在使用防护措施时,请确保遵循某些准则,以免混淆读者。
示例
// 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
;
答案 10 :(得分:-2)
当object为null等时抛出异常而不是返回任何内容。
你的方法期望对象不是null而不是这样,所以你应该抛出异常并让调用者处理它。
但是,早期的回归并不是坏事。