C#/JIT 编译器可能会删除包含将变量赋值到属性的行,以后不会使用该变量

本文关键字:变量 属性 赋值 编译器 JIT 删除 包含 | 更新日期: 2023-09-27 18:31:40

我有以下代码:

var ad = product.AttributesDictionary;
var ddl = product.DetailedDescriptionList;
var rdl = product.RelatedProductList;

我已经读到C#/JIT编译器可以做很多优化。由于赋值后不使用变量。我担心 C#/JIT 编译器可能会省略这些行以进行优化。

请不要告诉我删除此行。

更新:我认为没有人能够理解我的意思。我的问题是,如果您将属性分配给变量然后不使用此变量,C#/JIT 编译器是否会省略此代码?

C#/JIT 编译器可能会删除包含将变量赋值到属性的行,以后不会使用该变量

(这不是特定于C#编译器的,这只是一个一般的观察)

将属性分配给变量实际上包含两个部分:

  1. 执行属性的 get 方法
  2. 将方法的返回值分配给变量。

由于第一部分可以像任何方法一样复杂,并且有很多副作用,因此没有编译器会对其进行优化。

如果在赋值后未使用该变量,则可以优化第二部分,如果编译器正在积极优化。


但是,还有另一种优化器的行为不太可预测 - 下一个看到该代码的开发人员。

这些行看起来什么都不做,所以如果下一个看到它们的人是一个讨厌多余代码的学究(就像大多数开发人员一样),这些行可以删除而无需再看一眼。

尝试找到一种更好 - 更明确 - 的方式来完成您试图用这些线条完成的任何事情;

如果您尝试使用虚拟调用强制初始化蝇量级缓存,那么您可以尝试ToString(),就像我相信编译器不会省略该行一样product.AttributesDictionary.ToString();您可能需要检查product.AttributesDictionary是否为 null,具体取决于缓存的行为。

好吧,让我们看看规范对此有何说明:

第 3.10 节:执行顺序

C# 程序的执行继续进行,使得每个程序的副作用 执行线程保留在关键执行点。A面 效果定义为对易失性字段的读取或写入,对 非易失性变量、对外部资源的写入以及 引发异常。

[..]

此外,执行环境不需要计算 表达式,如果它可以推断出该表达式的值未被使用 并且不会产生所需的副作用(包括任何由 调用方法或访问易失性字段)。

由于您的代码显然可能会引发异常,因此如果优化了访问,这将是一个编译器错误。

因此,此代码在技术上是安全的。从访问者那里抛出例外不仅符合法律条文,而且符合精神:访问者是方法,方法当然可以预期抛出,这取决于具体情况。

但是,在此代码面向人类的一面,事情看起来并不好。如果有合理的期望,你想要检查某些内容,你的代码应该启用它:

obj.TrySomething()

它不应该要求您这样做:

try { obj.DoSomething(); }
catch (SomeException e) { ... }

可以通过将此代码编译到程序集(DLL 或 EXE)中,然后使用 ILDASM 将其转换为纯文本 IL 来找到答案。

这仍然不会确切地告诉您运行时会发生什么,因为 JIT 将 IL 转换为 x86/x64 机器代码。但是Visual Studio调试器会向你显示反汇编,允许你进一步挖掘。

我已经尝试过了,使用以下代码:

public class Example
{
    public string SomeProperty { get; set; }
    public static void DoSomething(Example instance)
    {
        var value = instance.SomeProperty;
    }
}

该方法DoSomething编译为以下 IL(由 ILSpy 提供):

.method public hidebysig static 
    void DoSomething (
        class Example 'instance'
    ) cil managed 
{
    // Method begins at RVA 0x2061
    // Code size 8 (0x8)
    .maxstack 8
    IL_0000: ldarg.0
    IL_0001: callvirt instance string Example::get_SomeProperty()
    IL_0006: pop
    IL_0007: ret
} // end of method Example::DoSomething

您将看到属性访问器get_SomeProperty()被调用,即使在可以编写的最基本的属性访问器的情况下,并且当类型在同一程序集中定义时也是如此。