否则还是回来?

以下两项中的哪一项最适合于绩效和标准实践。 .NET内部如何处理这两个代码片段?

代码1

If(result) { process1(); } else { process2(); } 

或代码2

 If(result) { process1(); return; } process2(); 

如果有的话,性能差异在任何正常情况下都可以忽略不计。

一种标准做法(除其他外)是试图从方法中保留一个退出点,以便讨论第一种选择。

中间return的实际实现最有可能跳转到方法的末尾,其中包含方法的堆栈帧的代码是,因此最终的可执行代码可能是两个相同的码。

就个人而言,我总是喜欢尽快回来,所以我会去做类似的事情:

 if (result) { // do something return; } // do something if not result 

关于性能,我怀疑它们是否具有任何优势,它实际上归结为可读性和个人品味。 我假设.NET会优化你的第一个代码块,如上所述。

我认为“单一退出点”被高估了。 过于教条地坚持它会导致一些非常复杂的代码,它们应该具有多个退出点,或者分成更小的方法。

我想说两者之间的选择取决于语义。

‘如果某些条件为真,则执行此操作,否则执行此操作’完美映射到if-else。

 if (isLoggedIn) { RedirectToContent(); } else { RedirectToLogin(); } 

‘如果某些条件然后进行一些清理和拯救’更好地映射到代码2.这称为保护模式。 这使得代码的主体尽可能正常,清晰和整洁,不必要的缩进。 它通常用于validation参数或状态(检查某些内容是否为null,或某些内容是否已缓存,类似于此类)。

 if (user == null) { RedirectToLogin(); return; } DisplayHelloMessage(user.Name); 

在同一个项目中使用这两种forms并不罕见。 正如我所说,使用哪种方法取决于您要传达的内容。

如果有公共代码,则需要在if / else块之后执行,然后选项1。

如果if-else块是函数中的最后一件事,那么选项2。

我个人总是使用选项1.如果有一个返回状态,它会在else块之后

如果您删除第二个版本的剩余代码周围的大括号,这正是我使用的。 我更喜欢使function的早期validation部分显而易见,然后我可以开始工作。

话虽如此,这是一个意见问题。 只要你对它保持一致,选择一个并坚持下去。

编辑:关于性能,发出的IL完全相同。 选择一种或另一种风格,任何一种都不会受到惩罚。

它们都将编译为相同的IL for Release模式(在Debug中可能会有一些不同的Nop操作数。)因此没有性能差异。 这完全取决于您和您的团队如何感觉代码更易于阅读。

我曾经在营地反对提前退出,但现在我觉得它可以使代码更容易遵循。

 // C# public static void @elseif(bool isTrue) { if (isTrue) Process1(); else Process2(); } // IL .method public hidebysig static void elseif(bool isTrue) cil managed { // Code size 15 (0xf) .maxstack 8 IL_0000: ldarg.0 IL_0001: brfalse.s IL_0009 IL_0003: call void elseif.Program::Process1() IL_0008: ret IL_0009: call void elseif.Program::Process2() IL_000e: ret } // end of method Program::elseif // C# public static void @earlyReturn(bool isTrue) { if (isTrue) { Process1(); return; } Process2(); } // IL .method public hidebysig static void earlyReturn(bool isTrue) cil managed { // Code size 15 (0xf) .maxstack 8 IL_0000: ldarg.0 IL_0001: brfalse.s IL_0009 IL_0003: call void elseif.Program::Process1() IL_0008: ret IL_0009: call void elseif.Program::Process2() IL_000e: ret } // end of method Program::earlyReturn 

不能说性能,但Code 1看起来更清晰,更符合逻辑。 跳出if块中间的函数看起来很混乱,很容易被忽视。

以下是对保护条款的一些补充说明: http : //www.c2.com/cgi/wiki?GuardClause

我没有看到的一个术语提到我认为很重要 – 保护条款的目的是提高可读性。 单一退出方法可以倾向于“箭头”代码(其中语句的嵌套构成箭头)。

返回将导致代码从if语句所处的任何方法返回,不再执行任何代码。 没有返回的语句将简单地从if语句中删除。

我会使用Code 1 ,因为如果我稍后在if语句之后添加一些内容,我仍然肯定会执行它,而不需要记住从Code 2删除return子句。

这两种风格都是司空见惯的,宗教战争也在争夺中。 🙂

我通常这样做:

  • 如果测试表达方法契约语义,例如检查输入参数的有效性,则选择选项2。
  • 否则,请选择选项1。

但是,可以说更重要的规则是“对于下一个查看代码的开发人员来说,哪个更具可读性和/或可维护性?”。

正如其他人所说,性能差异可以忽略不计。

我想你不应该担心这里的表现。 在这种情况下,可读性和可维护性更为重要。

坚持一个常规出口点是一个很好的做法。

但是,有时多次返回只会使代码更清晰,特别是当您在代码开头附近进行了大量测试(即检查所有输入参数是否格式正确)时,“if-true”应该导致回归。

即:

 if (date not set) return false; age = calculateAgeBasedOnDate(); if (age higher than 100) return false; ...lots of code... return result; 

我认为没关系。 你应该考虑可读性,我认为越少越好。 所以我会返回没有最后2个括号(见下面的示例)。 在写这样的东西时,请不要考虑性能,在过早的阶段,它不会给你任何东西,只会有太多的复杂性。

 if(result) { process 1 return; } process 2 

我认为第二种选择对于许多条件(例如validation)的情况更好。 如果你在这些情况下使用第一个,你会得到丑陋的缩进。

  if (con){ ... return; } if (other con){ ... return; } ... return; 

我倾向于有一个退出点,这在multithreading环境中实现锁时非常有用,以确保释放锁。 第一次实施时,更难做到。

我倾向于从函数中退出多个点。 我个人认为它更清晰,在某些情况下它可以更快。 如果您检查某些内容然后返回该程序将不执行任何左命令。 然后再次如HZC所说,如果您处理multithreading应用程序,那么您的最佳镜头可能正在使用您的第一个示例。 无论如何,对于一小段代码,它不会产生任何差别(可能甚至对于一些较大的代码也没有)。 最重要的是你写下你感觉舒服的方式。

如果我不得不说,我会说更好的做法是if-else而不是“隐含的”其他。 原因是,如果其他人修改了您的代码,他们可以通过浏览来轻松捕获它。

我记得编程世界中有一个关于你的代码中是否应该有多个return语句的争论。 可以说它是一个非常混乱的根源,因为如果你在“if”语句中有多个循环,并且有条件返回,那么它可能会引起一些混乱。

我通常的做法是使用if-else语句和单个return语句。

例如,

 type returnValue; if(true) { returnValue = item; } else returnValue = somethingElse; return returnValue; 

在我看来,上面的内容更具可读性。 然而,情况并非总是如此。 有时最好在if语句的中间有一个return语句,特别是如果它需要一个棘手的return语句。

这取决于“结果”,“process1”和“process2”是什么。

如果process2是“结果”为假的逻辑结果; 你应该去代码1.如果process1和process2是等价的替代品,这是最易读的模式。

 if (do_A_or_B = A) A() else B() 

如果结果是某些“禁止”状态而“process1”仅仅是在这种情况下需要清理的话,你应该选择代码2.如果“process2”是函数的主要动作,那么它是最可读的模式。

 if (NOT can_do_B) { A() return } B() 

这取决于具体情况。

如果这是在计算值时的函数,则在您拥有它时立即返回该值(选项2)。 您表明您已经完成了所需的所有工作,并且正在离开。

如果这是代码是程序逻辑的一部分,那么最好尽可能精确(选项1)。 看着选项1的人会知道你的意思(做这个或那个),因为选项2可能是一个错误(在这种情况下这样做,然后总是这样做 – 我会为你纠正它!)。 之前有过。

编译时这些通常是相同的,但我们对性能不感兴趣。 这是关于可读性和可维护性的。