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

时间:2008-10-08 23:44:33

标签: c# static dependencies internals

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

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

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

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

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

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

并且该程序运行不同(测试套装全面失败,我没有进一步观察)。

4 个答案:

答案 0 :(得分:16)

有关规则,请参阅section 10.4 of the C# spec

  

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

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

这些规则与Java不同(请参阅section 8.3.2.3 of the JLS了解有关前向引用的Java规则,这些规则更具限制性。)

答案 1 :(得分:14)

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

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

虽然此代码不起作用(它会抛出NullReferenceException

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

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


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

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] };
}

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

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); }

我们得到了这个输出:

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中的异常具有默认值(null)。由于anull,因此b也无法正确初始化。

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


编辑:如果您没有阅读评论,Jon Skeet提供了一个非常有趣的阅读:The differences between static constructors and type initializers

答案 2 :(得分:2)

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

static private List<int> a;
static private List<int> b;

static SomeClass()
{
    a = new List<int>() { 0 };
    b = new List<int>() { a[0] };
}

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

答案 3 :(得分:0)

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

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

会工作但是......

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

会失败。

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

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