布尔类型与内存的比较

4

我们在工作中讨论了代码设计问题,其中之一是处理调用布尔方法后的响应,比如:

bool ok = IsEverythingOK();

if(ok)
{
   //do somehthing
}

我的一个同事坚持认为我们可以跳过额外的变量ok,直接写

if(IsEverythingOK())
{
   //do somehthing
}

他说使用"bool ok"语句在内存方面不好。

那么我们应该使用哪个呢?


1
不必担心这个。这是微不足道的。 - Benjamin Diele
1
效率上没有区别。个人而言,我更喜欢第二种形式,因为我认为它更易读,但这只是我的观点。不过我可能会将该函数重命名为EverythingIsOK。 - 500 - Internal Server Error
1
@500-服务器内部错误 我觉得你把一个返回布尔值的函数命名成那样很奇怪。我工作过的所有地方都使用“IsSomethingValid”或其变体。 - Benjamin Diele
2
如果你曾经使用过调试器,悬停在“ok”变量上也非常方便 :) - marko
我认为第二个更易读,但从内存角度来看,它确实是相同的。至少如果你不是为烤面包机编写程序的话。 - Fabio Marcolini
1
当然,因为bool使用32位内存,最好不要创建局部变量。毕竟,在标准的8GB机器上,这将浪费可用内存的1/268,435,456。无论你做什么,都不要有25万个嵌套的if语句! - Enigmativity
4个回答

5

您的问题的意思是:

使用本地变量是否需要付费?

C#和.NET都是经过精心设计的,我的期望是像您描述的那样使用本地变量没有或者只有微不足道的成本,但让我试着通过一些事实来支持这个期望。

下面是C#代码:

if (IsEverythingOk()) {
  ...
}

将会编译成以下的IL代码(启用了优化):
call        IsEverythingOk
brfalse.s   AfterIfBody
... if body

使用局部变量

var ok = IsEverythingOk();
if (ok) {
  ...
}

您将获得这个经过优化(并简化)的IL代码:

call        IsEverythingOk
stloc.0
ldloc.0
brfalse.s   AfterIfBody
... if body

表面上看,这似乎不太高效,因为返回值存储在堆栈上,然后被检索,但 JIT 编译器也会执行一些优化。您可以通过启用本机代码调试来查看应用程序生成的实际机器代码。您必须使用发布版本进行此操作,并且还必须关闭抑制模块加载时 JIT 优化的调试器选项。现在,您可以在要检查的代码中设置断点,然后查看反汇编代码。请注意,JIT 就像黑匣子一样,我在我的计算机上看到的行为可能与其他人在其计算机上看到的不同。考虑到这一点,请注意两个版本的汇编代码都是(调用方式略有不同):
call        IsEverythingOk
test        eax,eax  
je          AfterIfBody

所以JIT将优化掉额外不必要的IL。实际上,在我的初始实验中,IsEverythingOk方法返回了true,并且JIT能够完全优化掉分支。当我切换到在该方法中返回字段时,JIT将内联调用并直接访问该字段。
底线:即使代码生成了一些看似不必要的额外IL,您也应该期望JIT至少优化简单的临时局部变量。

3

一切都取决于你在循环中是否使用ok

例如:

bool ok = IsEverythingOK();

if(ok)
{
   //do somehthing
   ok = IsEverythingOK();
}

假设在循环中您没有对ok进行任何操作,那么您可能会发现JIT编译器将会转换以下代码:
bool ok = IsEverythingOK();

if(ok)
{
   //do somehthing
}

...实质上变成了:

if(IsEverythingOK())
{
   //do somehthing
}

...anyway.


1
很抱歉,但这不是正确的。正如您在反汇编中所看到的,编译器不会优化此代码,尽管“ok”不再使用。事实上,使用“ok”会生成更多的代码。 - Fratyx

1

我认为这是个人偏好,除非你有一个统一的编码标准。其中一个提供了好处。

如果您预计或假定修改if子句之外的内容,那么这很棒。虽然它在创建变量时会创建堆栈条目,但它可能会在方法作用域后被处理掉。

bool ok = IsEverythingOK();

if(ok)
{
   //do somehthing
}

如果您只想将其用作验证,则这很棒。虽然仅适用于方法名称较短的情况。但是假设您在使用之前访问了一个类,例如_myLongNameInstance.IsEverythingOK(),这会降低可读性,我会选择第一个选项,但在其他条件下,我会选择直接if语句。
if(IsEverythingOK())
{
   //do somehthing
}

1
当然,如果您使用第一种解决方案,编译器会生成一些额外的IL代码步骤,因为它至少需要一个额外的“stloc”和“ldloc”命令,但是如果仅出于性能原因,请忘记这些微秒(或纳秒)。
如果没有其他原因需要“ok”变量,我仍然更喜欢第二种解决方案,因为它更易于阅读。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接