例如,当我在我的代码上运行ReSharper时:

    if (some condition)
    {
        Some code...            
    }

ReSharper给了我上面的警告(反转“if”语句以减少嵌套),并建议以下更正:

   if (!some condition) return;
   Some code...

我想知道为什么这样更好。我一直认为在方法中间使用“return”是有问题的,有点像“goto”。


当前回答

它不仅影响美观,而且还阻止了代码嵌套。

它实际上也可以作为确保数据有效的前提条件。

其他回答

就我个人而言,我倾向于只有一个退出点。如果您保持方法的简短和切中要害,这很容易实现,并且它为下一个处理您的代码的人提供了可预测的模式。

eg.

 bool PerformDefaultOperation()
 {
      bool succeeded = false;

      DataStructure defaultParameters;
      if ((defaultParameters = this.GetApplicationDefaults()) != null)
      {
           succeeded = this.DoSomething(defaultParameters);
      }

      return succeeded;
 }

如果您只是想在函数退出之前检查函数中某些局部变量的值,这也是非常有用的。您所需要做的就是在最终返回值上放置一个断点,并保证能够命中它(除非抛出异常)。

避免多个退出点可以提高性能。我对c#不太确定,但在c++中,命名返回值优化(复制省略,ISO c++ '03 12.8/15)依赖于有一个单一的出口点。这种优化避免了复制构造返回值(在您的特定示例中,这无关紧要)。在紧密循环中,这可能会大大提高性能,因为每次调用函数时都保存了一个构造函数和一个析构函数。

但是在99%的情况下,节省额外的构造函数和析构函数调用并不值得损失嵌套的可读性,如果块引入(正如其他人所指出的)。

关于代码的外观有很多很好的理由。但是结果如何呢?

让我们来看看一些c#代码和它的IL编译形式:

using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length == 0) return;
        if ((args.Length+2)/3 == 5) return;
        Console.WriteLine("hey!!!");
    }
}

可以编译这个简单的代码片段。您可以使用ildasm打开生成的.exe文件并检查结果。我不会发布所有汇编程序的东西,但我会描述结果。

生成的IL代码执行以下操作:

如果第一个条件为假,则跳转到第二个条件所在的代码。 如果为真,则跳转到最后一条指令。(注意:最后一个指令是return)。 在第二种情况下,计算结果后也会发生同样的情况。比较和:到达控制台。如果为假则写eline,如果为真则写到结尾。 打印消息并返回。

所以看起来代码会跳到最后。如果我们对嵌套代码做一个正常的if呢?

using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length != 0 && (args.Length+2)/3 != 5) 
        {
            Console.WriteLine("hey!!!");
        }
    }
}

结果在IL指令中非常相似。不同之处在于,之前每个条件有两次跳转:如果为假则跳转到下一段代码,如果为真则跳转到最后一段代码。现在IL代码流得更好了,有3个跳跃(编译器优化了一点):

第一次跳转:当Length为0到代码再次跳转(第三次跳转)的部分时。 第二:在第二种情况中间避免一条指令。 第三:如果第二个条件为假,跳到最后。

不管怎样,程序计数器总是会跳转。

在我看来,如果你只是返回void(或一些你永远不会检查的无用的返回代码),那么早期返回是很好的,它可能会提高可读性,因为你避免了嵌套,同时你显式地表明你的函数已经完成。

如果你实际上返回一个returnValue,嵌套通常是一个更好的方式,因为你只在一个地方返回你的returnValue(在末尾),它可能会使你的代码在很多情况下更易于维护。

There are several advantages to this sort of coding but for me the big win is, if you can return quick you can improve the speed of your application. IE I know that because of Precondition X that I can return quickly with an error. This gets rid of the error cases first and reduces the complexity of your code. In a lot of cases because the cpu pipeline can be now be cleaner it can stop pipeline crashes or switches. Secondly if you are in a loop, breaking or returning out quickly can save you a lots of cpu. Some programmers use loop invariants to do this sort of quick exit but in this you can broke your cpu pipeline and even create memory seek problem and mean the the cpu needs to load from outside cache. But basically I think you should do what you intended, that is end the loop or function not create a complex code path just to implement some abstract notion of correct code. If the only tool you have is a hammer then everything looks like a nail.