C#中类之间静态变量初始化顺序是什么?

11
3个回答

28

如果不会陷入死循环,一个类型依赖于另一个类型的初始化是可以接受的。

基本上这是可行的:

public class Child
{
    static Child() {} // Added static constructor for extra predictability
    public static readonly int X = 10;
}

public class Parent
{
    static Parent() {} // Added static constructor for extra predictability
    public static readonly int Y = Child.X;
}

结果是定义明确的。Child类的静态变量初始化器在根据规范第10.5.5.1节中任何静态字段的首次访问之前执行。

这个不是。

public class Child
{
    public static readonly int Nasty = Parent.Y;
    public static readonly int X = 10;
}

public class Parent
{
    public static readonly int Y = Child.X;
}
在后一种情况下,你将得到 Child.Nasty=0Parent.Y=10Child.X=10 或者 Child.Nasty=0Parent.Y=0Child.X=10 的值,取决于首先访问哪个类。Parent.Y first 的访问会先初始化 Parent,然后触发 Child 的初始化。而当Child.X 被初始化之前就使用了它的值来赋给 Parent.Y,所以 Child 知道必须初始化 Parent,但是CLR知道正在初始化 Parent,所以继续执行,导致出现了第一组数字。而访问 Child.Nasty 会先初始化 Child,然后开始初始化 Parent。在这种情况下,初始化 Parent 将意识到必须初始化 Child,但CLR知道正在初始化 Child,所以继续执行,导致出现第二组数字。 不要这样做。

@新手入门:这很有用,但不是相关的。我会将其添加为子注释。 - Jon Skeet
@Jon Skeet: 现在我明白了。非常感谢你。 - user151323
当使用多个源文件的部分类时,静态字段初始化的顺序似乎是基于将源文件添加到.csproj的编译包含中的顺序(然后按每个文件中的文本顺序)。因此,如果在文件B中的静态字段初始化中引用了来自文件A的静态字段(两者都属于同一个部分类),请确保在csproj中先将文件A编译包含,然后再编译包含文件B。 - Alon Gingold
仅阅读MSDN-似乎与@JonSkeet相矛盾,因为MSDN称静态成员首先初始化,MSDN说“自动调用静态构造函数。在创建第一个实例之前或引用任何静态成员之前,它将初始化类。”所以我理解的方式是,在静态成员之前,首先调用静态构造函数。我感到困惑。 - Vidar
1
@Vidar:如果一个类型有静态构造函数,那么变量的静态初始化器实际上会被提升到那里。我不确定静态初始化器和静态构造函数中的代码顺序是否有保证。但是你引用的内容是关于触发类初始化的,而不是各种初始化方面发生的顺序。 - Jon Skeet
显示剩余5条评论

0

如果您关心顺序,您可以将代码放置在静态构造函数中。这是我注册依赖属性的地方。


0

不,我认为“unreliable”这个词在这里并不正确。

在真正的单线程场景中,当您的代码中访问类型的任何静态成员时,类的静态成员将被初始化。

我不了解c++,但是在多线程环境中只有在某些情况下,如果两个类型尝试访问共享资源并且该资源为静态资源,则无法确定哪个线程会胜出,也无法确定哪个线程会正确工作。

MSDN示例是正确的,它将正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接