14

我发现静态字段初始化可以表现不同。对于以下代码,

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 优化,动机是什么?

4

3 回答 3

7

它完全取决于静态初始化程序何时执行的实现。所以顺序可能不同。但是如果你提供一个静态构造函数,那些静态初始化器总是会更早执行。因此,输出将是一致的。

来自MSDN

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

在类中添加静态构造函数Singleton

static Singleton() { }
于 2012-05-27T15:58:36.873 回答
2

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

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

于 2012-05-27T16:18:16.850 回答
1

另一个答案中,我提供了一个测试程序并讨论了{ Debug, Release }{ 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

于 2017-12-22T01:50:23.140 回答