在void方法中使用return是不好的做法吗?

时间:2009-08-16 02:06:00

标签: c# return void

想象一下以下代码:

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

    DoThat();
}

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

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

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

11 个答案:

答案 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)

在使用防护措施时,请确保遵循某些准则,以免混淆读者。

  • 函数做一件事
  • 后卫仅作为函数中的 first 逻辑引入
  • 未嵌套部分包含函数的核心意图

示例

// 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而不是这样,所以你应该抛出异常并让调用者处理它。

但是,早期的回归并不是坏事。