静态字段初始化如何在C#中工作?

是否应该在调用构造函数之前完成静态字段初始化?

以下程序提供的输出似乎对我不正确。

new A() _A == null static A() new A() _A == A 

代码:

 public class A { public static string _A = (new A()).I(); public A() { Console.WriteLine("new A()"); if (_A == null) Console.WriteLine("_A == null"); else Console.WriteLine("_A == " + _A); } static A() { Console.WriteLine("static A()"); } public string I() { return "A"; } } class Program { static void Main(string[] args) { var a = new A(); } } 

这是对的。

你的静态初始化器,然后静态构造函数在你的标准构造函数之前运行,但是当它运行时,它使用新的A(),所以通过你的非静态构造函数路径。 这会导致您看到的消息。

这是完整的执行路径:

当你第一次调用var a = new A(); 在您的程序中,这是第一次访问A.

这将触发A._A的静态初始化

此时,A._A构造有_A = (new A()).I();

这点击

 Console.WriteLine("new A()"); if (_A == null) Console.WriteLine("_A == null"); 

从那时起,_A尚未使用返回的构造类型(尚未)进行设置。

接下来,静态构造函数A { static A(); } 运行。 这将打印“静态A()”消息。

最后,执行您的原始语句( var a = new A(); ),但此时,构造静态,以便获得最终打印。

一个额外的注意事项–C#规范(我看4.0,但它也在3.0中)在10.5.5.1静态字段初始化中说:

如果类中存在静态构造函数(第10.12节),则在执行该静态构造函数之前立即执行静态字段初始值设定项。 否则,静态字段初始化器在第一次使用该类的静态字段之前的实现相关时间执行。

您有一个静态构造函数,因此“OTHER”子句不适用。 但我认为,如果您没有静态构造函数,那么您的问题就是相关信息,静态字段初始化程序可以在“依赖于实现的时间”执行。 如果静态字段初始化程序正在执行某种类型的数据初始化或对象创建,而不依赖于静态字段本身,则这可能很重要。

我猜,它是深奥的,但我看到它发生在今天,因为“实现依赖时间”似乎在C#3.0和4.0之间发生了变化 – 至少对于我正在考虑的情况而言。 简单的解决方案当然很简单 – 只需添加一个静态构造函数……

我实际上相信它正在做你的想法。 你的测试很难分辨。

你_A的初始化

 public static string _A = (new A()).I(); 

首先创建一个新的A实例,因此你的新A()和_A = null的着作。 因为它在启动时为null,因为这是初始化。 初始化后,将调用静态构造函数,该构造函数将返回新实例。

似乎编译器正在做预期的事情。

1st – 在类中执行所有静态代码(首先是字段,然后是静态构造函数):

 public static string _A = (new A()).I(); // and static A() { Console.WriteLine("static A()"); } 

第二类 – 构造函数被调用:

 public A() { Console.WriteLine("new A()"); if (_A == null) Console.WriteLine("_A == null"); else Console.WriteLine("_A == " + _A); } 

你问为什么这是可能的。 嗯,在我看来,一个实例并不绝对要求在创建时初始化所有类变量。 它只需要它们必须存在。 我认为这个特殊情况支持这种想法,因为在完成所有静态初始化之前创建了一个实例。

是的,静态字段初始化应该在调用构造函数之前完成。 但是你把编译器置于exception情况下它就不能遵守这个规则。

这是一个有趣的技巧,但它不会在正常的应用程序中发生。