C#中的静态构造函数/初始化程序的顺序

在使用C#应用程序时,我只是注意到在几个地方静态初始化程序彼此依赖,如下所示:

static private List a = new List() { 0 }; static private List b = new List() { a[0] }; 

没有做任何有用的特殊工作。 这只是运气吗? C#有解决这个问题的规则吗?

编辑:( re:Panos)在一个文件中,词汇顺序似乎是王道? 跨文件怎么样?

在寻找我尝试这样的周期性依赖:

 static private List a = new List() { b[0] }; static private List b = new List() { a[0] }; 

并且该程序没有运行相同(测试套装全面失败,我没有看得更远)。

它似乎取决于线的顺序。 此代码有效:

 static private List a = new List() { 1 }; static private List b = new List() { a[0] }; 

虽然这段代码不起作用(它抛出NullReferenceException

 static private List a = new List() { b[0] }; static private List b = new List() { 1 }; 

所以,显然没有关于周期性依赖的规则。 然而,奇怪的是编译器没有抱怨……


编辑 – “跨文件”发生了什么? 如果我们声明这两个类:

 public class A { public static List a = new List() { Bb[0] }; } public class B { public static List b = new List() { Aa[0] }; } 

并尝试使用以下代码访问它们:

 try { Console.WriteLine(Bb); } catch (Exception e) { Console.WriteLine(e.InnerException.Message.); } try { Console.WriteLine(Aa); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); } try { Console.WriteLine(Bb); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); } 

我们得到这个输出:

 The type initializer for 'A' threw an exception. Object reference not set to an instance of an object. The type initializer for 'A' threw an exception. 

因此, B的初始化会导致静态构造函数A和lefts字段a的exception具有默认值(null)。 由于anull ,因此b也无法正确初始化。

如果我们没有周期性依赖,一切正常。


编辑:为了防止你没有阅读评论, Jon Skeet提供了一个非常有趣的阅读: 静态构造函数和类型初始值设定项之间的区别 。

有关规则,请参阅C#规范的10.4节 :

初始化类时,首先将该类中的所有静态字段初始化为其默认值,然后以文本顺序执行静态字段初始值设定项。 同样,当创建类的实例时,首先将该实例中的所有实例字段初始化为其默认值,然后以文本顺序执行实例字段初始化程序。 具有可变初始值设定项的静态字段可以在其默认值状态下被观察到。 然而,作为一种风格问题,强烈建议不要这样做。

换句话说,在您的示例中,’b’被初始化为其默认状态(null),因此在’a’的初始化程序中对它的引用是合法的,但会导致NullReferenceException。

这些规则与Java不同(请参阅JLS for Java关于前向引用的规则的第8.3.2.3节,这些规则更具限制性)。

我个人会删除静态初始化程序,因为它不清楚并添加一个静态构造函数来初始化这些变量。

 static private List a; static private List b; static SomeClass() { a = new List() { 0 }; b = new List() { a[0] }; } 

然后你不必猜测发生了什么,你的意图是明确的。

是的,你很幸运。 C#似乎按照它在类中出现的顺序执行代码。

 static private List a = new List() { 0 }; static private List b = new List() { a[0] }; 

会工作,但……

 static private List b = new List() { a[0] }; static private List a = new List() { 0 }; 

将失败。

我建议将所有依赖项放在一个地方,静态构造函数就是这个地方。

 static MyClass() { a = new List() { 0 }; b = new List() { a[0] }; }