以下两项中哪一项最适合表现和标准练习。 .NET内部如何处理这两个代码片段?
代码1
If(result)
{
process1();
}
else
{
process2();
}
或代码2
If(result)
{
process1();
return;
}
process2();
答案 0 :(得分:32)
就我个人而言,我总是希望尽快回来,所以我会选择:
if (result)
{
// do something
return;
}
// do something if not result
关于性能,我怀疑它们是否具有任何优势,它实际上取决于可读性和个人品味。我假设.NET会优化你的第一个代码块,如上所述。
答案 1 :(得分:25)
在任何正常情况下,性能差异(如果有的话)都可以忽略不计。
一种标准做法(除此之外)是尝试从方法中保留一个退出点,以便讨论第一种选择。
中间的return
的实际实现最有可能跳转到方法的末尾,其中包含方法的堆栈帧的代码是,所以它很可能是最终的两个代码的可执行代码相同。
答案 2 :(得分:23)
我认为'单一退出点'被高估了。过于教条地坚持它会导致一些非常复杂的代码,它们应该有多个出口点,或者分成更小的方法。
我想说两者之间的选择取决于语义。
'如果某些条件为真则执行此操作,否则执行此操作'完美映射到if-else。
if (isLoggedIn) {
RedirectToContent();
} else {
RedirectToLogin();
}
'如果某些条件然后进行一些清理并拯救'更好地映射到代码2.这称为保护模式。这使得代码的主体尽可能正常,清晰,整洁,不必要的缩进。它通常用于验证参数或状态(检查某些内容是否为空,或某些内容是否已缓存,类似于此类)。
if (user == null) {
RedirectToLogin();
return;
}
DisplayHelloMessage(user.Name);
在同一个项目中看到这两种形式并不少见。正如我所说,使用哪种方法取决于你要传达的内容。
答案 3 :(得分:4)
如果有公共代码,则需要在if / else块之后执行,然后选项1。
如果if-else块是函数中的最后一件事,那么选项2。
我个人总是使用选项1.如果有一个返回状态,它会在else块
之后出现答案 4 :(得分:3)
如果你删除第二个版本的剩余代码周围的大括号,那正是我使用的。我更倾向于使函数的早期验证部分显而易见,然后我就可以开始工作了。
话虽如此,这是一个意见问题。只要你对它保持一致,选择一个并坚持下去。
编辑关于性能,发出的IL完全相同。选择一种或另一种风格,也不会受到任何惩罚。
答案 5 :(得分:3)
它们都将编译为相同的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
答案 6 :(得分:2)
不能说性能,但代码1看起来更清晰,更符合逻辑。跳出if
块中间的函数看起来很混乱,很容易被忽视。
答案 7 :(得分:2)
以下是对保护条款的一些补充说明:http://www.c2.com/cgi/wiki?GuardClause
我认为没有提到的一个术语我认为很重要 - 保护条款的目的是提高可读性。单一退出方法可以倾向于“箭头”代码(其中语句的嵌套构成箭头)。
答案 8 :(得分:1)
这两种风格都是司空见惯的,宗教战争已经在他们身上展开。 : - )
我通常这样做:
然而,可以说更重要的规则是“对于下一个查看代码的开发人员来说,哪个更具可读性和/或可维护性?”。
正如其他人所说,性能差异可以忽略不计。
答案 9 :(得分:1)
我认为你不应该担心这里的表现。在这种情况下,可读性和可维护性更为重要。
坚持一个常规出口点是一种很好的做法。
然而,有时多次返回只会使代码更清晰,特别是当您在代码开头附近进行了大量测试时(即检查所有输入参数是否格式正确),其中'if -true'应该导致回归。
即:
if (date not set) return false;
age = calculateAgeBasedOnDate();
if (age higher than 100) return false;
...lots of code...
return result;
答案 10 :(得分:1)
我会使用Code 1
,因为如果我在if
语句之后添加一些内容,我仍然肯定它会执行,而我不需要记住删除return
子句来自Code 2
。
答案 11 :(得分:1)
返回将导致代码从if语句所在的任何方法返回,不再执行任何代码。没有返回的语句将简单地从if语句中删除。
答案 12 :(得分:0)
我认为第二种选择对于许多条件(例如验证)的情况更好。 如果你在这些情况下使用第一个,你会得到丑陋的缩进。
if (con){
...
return;
}
if (other con){
...
return;
}
...
return;
答案 13 :(得分:0)
我倾向于有单点退出,这在多线程环境中实现锁时非常有用,以确保释放锁。第一次实施时,更难做到。
答案 14 :(得分:0)
我倾向于从函数中退出多个点。我个人认为它更清晰,在某些情况下它可以更快。如果您检查某些内容然后返回该程序将不执行任何左命令。 然后再次如HZC所说,如果您处理多线程应用程序,那么您最好的镜头可能正在使用您的第一个示例。无论如何,对于一小段代码,它不会有任何区别(可能甚至对于一些较大的代码也没有)。最重要的是你写下你感觉舒服的方式。
答案 15 :(得分:0)
我认为没关系。你应该考虑可读性,我认为越少越好。所以我会返回没有最后2个括号(见下面的示例)。 在写这样的东西时,请不要考虑性能,它不会给你任何东西,但在过早的阶段会给你太多的复杂性。
if(result)
{
process 1
return;
}
process 2
答案 16 :(得分:0)
如果我不得不说,我会说更好的做法是if-else而不是“隐含的”其他。原因在于,如果其他人修改了您的代码,他们可以通过浏览来轻松捕获它。
我记得编程世界中有一个关于你的代码中是否应该有多个return语句的争论。可以说它是一个非常混乱的根源,因为如果你在“if”语句中有多个循环,并且有条件返回,那么它可能会引起一些混乱。
我通常的做法是使用if-else语句和单个return语句。
例如,
type returnValue;
if(true)
{
returnValue = item;
}
else
returnValue = somethingElse;
return returnValue;
在我看来,上面的内容更具可读性。然而,情况并非总是如此。有时最好在if语句的中间使用return语句,特别是如果它需要一个棘手的return语句。
答案 17 :(得分:0)
取决于“结果”,“process1”和“process2”是什么。
如果process2是“结果”为假的逻辑结果;你应该去代码1.如果process1和process2是等价的替代品,这是最易读的模式。
if (do_A_or_B = A)
A()
else
B()
如果结果是某些“禁止”状态而“process1”仅仅是在这种情况下需要清理的话,你应该选择代码2.如果“process2”是函数的主要动作,那么它是最易读的模式。 / p>
if (NOT can_do_B)
{
A()
return
}
B()
答案 18 :(得分:0)
这取决于具体情况。
如果这是在计算值时的函数,则在您拥有它时立即返回该值(选项2)。您正在展示您已经完成了所需的所有工作,并且正在离开。
如果这是代码是程序逻辑的一部分,那么最好尽可能精确(选项1)。看着选项1的人会知道你的意思(做这个或那个),其中选项2可能是一个错误(在这种情况下这样做,然后总是这样做 - 我会为你纠正它!)。之前就已经过了。
编译时这些通常是相同的,但我们对性能不感兴趣。这是关于可读性和可维护性。