布尔值与内存

本文关键字:内存 布尔值 | 更新日期: 2023-09-27 18:29:53

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

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

我的一位同事坚持我们跳过额外的变量ok,写

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

既然他说使用"bool ok"语句在记忆方面是不好的。

哪一个是我们应该使用的?

布尔值与内存

解释您的问题:

使用局部变量是否有成本?

因为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将内联调用并直接访问字段。

最重要的是:您应该期望JIT至少优化一些简单的事情,比如瞬态局部变量,即使代码生成了一些看起来不必要的额外IL。

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

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

然而,假设您在循环中不使用ok,您可能会发现JIT编译器将变成:

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

本质上是什么:

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

无论如何

如果使用第一个解决方案,编译器当然会生成一些额外的IL代码步骤,因为它至少需要一个额外的stloc和一个ldloc命令,但如果只是出于性能原因,请忘记这些微秒(或纳秒)。

如果ok变量没有其他原因,我还是更喜欢第二种解决方案,因为它更容易阅读。

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

如果您期望或假设除if子句之外的其他修改,这将是非常好的。尽管它确实在创建变量时创建了一个堆栈条目,但它将在方法作用域之后被大量释放。

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

如果您只想将其用作验证,那么这将非常棒。尽管只有当您的方法名称较短时才有好处。但假设你在使用类之前访问它,比如_myLongNameInstance.IsEverythingOK()这会降低可读性,我会使用第一个,但在其他条件下,我会选择直接if.

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