98

想象一下下面的代码:

void DoThis()
{
    if (!isValid) return;

    DoThat();
}

void DoThat() {
    Console.WriteLine("DoThat()");
}

在 void 方法中使用 return 可以吗?它有任何性能损失吗?或者写这样的代码会更好:

void DoThis()
{
    if (isValid)
    {
        DoThat();
    }
}
4

11 回答 11

183

void 方法中的 return 还不错,这是一种常见的做法,即反转if语句以减少嵌套

减少对方法的嵌套可以提高代码的可读性和可维护性。

实际上,如果您有一个没有任何 return 语句的 void 方法,编译器将始终在其末尾生成一个ret 指令。

于 2009-08-16T02:11:51.237 回答
33

使用守卫(与嵌套代码相反)还有另一个重要原因:如果另一个程序员将​​代码添加到您的函数中,他们将在更安全的环境中工作。

考虑:

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

显然这是一个简单的案例,但程序员在第一个(嵌套代码)实例中添加了程序崩溃。在第二个示例中(使用警卫提前退出),一旦您越过了警卫,您的代码就不会意外使用空引用。

当然,一个伟大的程序员不会(经常)犯这样的错误。但是预防胜于治疗——我们可以以一种完全消除这种潜在错误来源的方式编写代码。嵌套增加了复杂性,因此最佳实践建议重构代码以减少嵌套。

于 2009-08-16T06:52:22.263 回答
19

坏习惯???没门。事实上,如果验证失败,最好尽早从方法返回来处理验证。否则会导致大量嵌套的 if 和 else。提前终止可以提高代码的可读性。

还要检查对类似问题的回答:我应该使用 return/continue 语句而不是 if-else 吗?

于 2009-08-16T03:36:12.470 回答
8

第一个示例是使用保护语句。来自维基百科

在计算机编程中,守卫是一个布尔表达式,如果程序要在相关分支中继续执行,则该表达式必须计算为真。

我认为在方法的顶部有一堆守卫是一种完全可以理解的编程方式。它基本上是说“如果其中任何一个为真,请不要执行此方法”。

所以一般来说它会是这样的:

void DoThis()
{
  if (guard1) return;
  if (guard2) return;
  ...
  if (guardN) return;

  DoThat();
}

我认为这更具可读性:

void DoThis()
{
  if (guard1 && guard2 && guard3)
  {
    DoThat();
  }
}
于 2009-08-16T06:07:22.107 回答
8

这不是不好的做法(出于已经说明的所有原因)。但是,方法中的回报越多,就越有可能将其拆分为更小的逻辑方法。

于 2009-08-16T04:35:56.727 回答
3

没有性能损失,但是第二段代码更具可读性,因此更易于维护。

于 2009-08-16T02:08:43.273 回答
2

在这种情况下,您的第二个示例是更好的代码,但这与从 void 函数返回无关,这仅仅是因为第二个代码更直接。但是从 void 函数返回完全没问题。

于 2009-08-16T04:22:40.580 回答
0

我不同意你们所有年轻的whippersnappers在这个问题上。

在方法的中间使用 return ,不管是无效的还是其他的,都是非常糟糕的做法,原因很清楚,近四十年前,已故的 Edsger W. Dijkstra 从著名的“GOTO 语句被认为是有害的”开始”,并继续阅读 Dahl、Dijkstra 和 Hoare 所著的“结构化编程”。

基本规则是每个控制结构和每个模块都应该有一个入口和一个出口。模块中间的显式返回打破了该规则,并且使推理程序状态变得更加困难,这反过来又使判断程序是否正确变得更加困难(这是一个更强大的属性而不是“它是否看起来有效”)。

“GOTO 语句被认为是有害的”和“结构化编程”开启了 1970 年代的“结构化编程”革命。这两部分是我们今天拥有 if-then-else、while-do 和其他显式控制结构的原因,也是高级语言中的 GOTO 语句被列入濒危物种名单的原因。(我个人的意见是它们需要在灭绝物种名单上。)

值得注意的是,消息流调制器是第一次尝试通过验收测试的第一款军事软件,没有任何偏差、弃权或“是的,但是”的措辞,它是用一种甚至没有的语言编写的GOTO 语句。

还值得一提的是,Nicklaus Wirth 在 Oberon 编程语言的最新版本 Oberon-07 中改变了 RETURN 语句的语义,使其成为类型化过程(即函数)声明的尾部,而不是函数体中的可执行语句。他对更改的解释说,他这样做正是因为以前的形式违反了结构化编程的单出口原则。

于 2009-08-16T05:55:45.130 回答
0

完全没问题,没有“性能损失”,但永远不要写没有括号的“if”语句。

总是

if( foo ){
    return;
}

它更具可读性;而且您永远不会意外地假设代码的某些部分不在该语句中。

于 2009-08-16T02:16:00.277 回答
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
;
于 2020-09-03T13:30:05.483 回答
-3

当对象为空等时抛出异常而不是什么也不返回。

您的方法期望 object 不是 null 并且不是这种情况,因此您应该抛出异常并让调用者处理它。

但是提前返回不是坏习惯。

于 2016-10-25T13:23:48.227 回答