静态字段 调试和发布版本中的初始化

本文关键字:版本 初始化 调试 字段 布版本 静态 | 更新日期: 2023-09-27 18:35:24

我发现静态字段初始化的行为可能有所不同。 对于以下代码,

public class Class1
{
  public static void Main()
  {
    Console.WriteLine("Main");
    Test();
    Console.ReadLine();
  }
  public static void Test(){
    Console.WriteLine("Test");
    Singleton.Instance.DoSomething();
  }
}
public class Singleton
{
  private static Singleton sInstance = new Singleton();
  protected Singleton()
  {
    Console.WriteLine("Singleton Constructor");
  }
  public static Singleton Instance
  {
    get
    {
      return sInstance;
    }
  }
  public void DoSomething(){}
}

在调试版本中,它将打印

Main
Test
Singleton Constructor

在发布版本中,它将打印

Main
Singleton Constructor
Test

我检查了这两个构建生成的 IL 代码,几乎相同。

我想知道这是怎么发生的?如果它是发布版本中的一种 JIT 优化,动机是什么?

静态字段 调试和发布版本中的初始化

这完全取决于何时执行静态初始值设定项的实现。所以顺序可能会有所不同。但是,如果您提供静态构造函数,这些静态初始值设定项将始终更早执行。因此,输出将按一致的顺序排列。

来自 MSDN

的静态字段变量初始值设定项对应于按它们在类声明中出现的文本顺序执行的赋值序列。如果类中存在静态构造函数(第 10.11 节),则静态字段初始值设定项的执行将在执行该静态构造函数之前立即发生。否则,静态字段初始值设定项将在首次使用该类的静态字段之前在与实现相关的时间执行。

Singleton 类中添加静态构造函数

static Singleton() { }

我在发布版本中重现了x86抖动。 这是设计使然,不能保证静态类构造函数运行的确切时刻,唯一的要求是它发生在类运行中的任何其他方法之前。 因此,如果优化器生成更有效的代码,则允许优化器重新排列代码。 确实如此,它将 cctor 调用一直移回 Main() 方法。 优点是它现在有更多机会优化剩余代码。

通常,您希望避免在具有可观察到的副作用的初始化表达式中编写代码。

在另一个答案中,我提供了一个测试程序,并讨论了{调试,发布}{x86,x64}和.NET主要版本之间的比较。总结结果如下;有关完整详细信息,请查看链接:

.NET 2.0/3.5


2.0.50727.8825 x86 Debug
2.0.50727.8825 x86 Debug TouchMe fieldinit
2.0.50727.8825 x86 Release fieldinit
2.0.50727.8825 x86 Release TouchMe fieldinit
2.0.50727.8825 x64 Debug
2.0.50727.8825 x64 Debug TouchMe fieldinit
2.0.50727.8825 x64 Release
2.0.50727.8825 x64 Release TouchMe fieldinit

.NET 4.7.1


4.7.2556.0 x86 Debug
4.7.2556.0 x86 Debug TouchMe fieldinit
4.7.2556.0 x86 Release
4.7.2556.0 x86 Release TouchMe
4.7.2556.0 x64 Debug
4.7.2556.0 x64 Debug TouchMe fieldinit
4.7.2556.0 x64 Release
4.7.2556.0 x64 Release TouchMe