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

BCS*_*BCS 20 c# dependencies static internals

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

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };
Run Code Online (Sandbox Code Playgroud)

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

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

看起来我尝试了这样的周期性依赖:

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { a[0] };
Run Code Online (Sandbox Code Playgroud)

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

Cow*_*wan 17

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

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

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

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


Pan*_*nos 14

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

static private List<int> a = new List<int>() { 1 };
static private List<int> b = new List<int>() { a[0] };
Run Code Online (Sandbox Code Playgroud)

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

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { 1 };
Run Code Online (Sandbox Code Playgroud)

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


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

public class A {
    public static List<int> a = new List<int>() { B.b[0] };
}
public class B {
    public static List<int> b = new List<int>() { A.a[0] };
}
Run Code Online (Sandbox Code Playgroud)

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

try { Console.WriteLine(B.b); } catch (Exception e) { Console.WriteLine(e.InnerException.Message.); }
try { Console.WriteLine(A.a); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); }
try { Console.WriteLine(B.b); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); }
Run Code Online (Sandbox Code Playgroud)

我们得到这个输出:

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.
Run Code Online (Sandbox Code Playgroud)

因此初始化B会导致静态构造函数A和lefts字段中的异常a具有默认值(null).既然anull,b也不能正确初始化.

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


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

  • 这里要小心静态变量初始化器和静态构造器之间的区别.基于静态构造函数的存在/不存在,何时发生类型初始化有不同的规则.请参见http://pobox.com/~skeet/csharp/beforefieldinit.html (2认同)