我发现静态字段初始化的行为可能不同。对于以下代码,
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优化,那么动机是什么?
答案 0 :(得分:5)
完全取决于执行静态初始化程序时的实现。所以订单可能会有所不同。但是如果你提供一个静态构造函数,那些静态初始化程序将始终更早地执行。因此输出将按照一致的顺序。
来自MSDN
类的静态字段变量初始值设定项对应于按照它们出现在类声明中的文本顺序执行的赋值序列。如果类中存在静态构造函数(第10.11节),则在执行该静态构造函数之前立即执行静态字段初始值设定项。否则,静态字段初始化器在第一次使用该类的静态字段之前的实现相关时间执行。
在Singleton
类
static Singleton() { }
答案 1 :(得分:1)
我在发布版本中重新编写了x86 jitter。这是设计上的,没有保证静态类构造函数运行的确切时刻,唯一的要求是它在之前发生类中的任何其他方法运行。因此,如果生成更高效的代码,则允许优化器重新排列代码。确实如此,它将cctor调用一直移回Main()方法。它的优势在于它现在有更多优化剩余代码的机会。
通常,您希望避免在具有可观察副作用的初始化表达式中编写代码。
答案 2 :(得分:0)
在another answer中,我提供了一个测试程序,并讨论了 {Debug,Release} , {x86,x64} 和之间的比较.NET 主要版本。总结结果如下;有关详细信息,请查看link:
.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