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

DependencyProperty.AddOwner MSDN页面提供了一个示例,其中包含两个具有静态成员的类,并且一个类的成员依赖于另一个类的成员进行初始化。 我认为MSDN是错误的 – 静态变量的初始化顺序在C#中是不可靠的, 就像在C ++或其他任何地方一样。 我可能错了,因为WPF库本身就是这样编写的,它运行得很好。 我错过了什么? C#编译器如何知道安全初始化顺序?

一种类型依赖于另一种被初始化的类型是好的,只要你不在一个循环中结束。

基本上这很好:

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

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

但事实并非如此:

 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.Y first将首先开始初始化ParentChild的初始化将意识到Parent需要被初始化,但是CLR知道它已经被初始化,所以无论如何继续,导致第一组数字 – 因为Child.X最终在其值被用于之前被初始化Parent.Y

访问Child.Nasty将首先开始初始化Child ,然后开始初始化ParentParent的初始化将意识到Child需要被初始化,但CLR知道它已经被初始化,因此无论如何继续,导致第二组数字。

不要这样做。


编辑:好的,更详细的解释,如承诺。

何时初始化类型?

如果类型具有静态构造函数 ,则只有在首次使用时才会初始化它(无论是引用静态成员还是创建实例时)。 如果它没有静态构造函数,则可以初始化earler。 理论上它也可以在以后初始化; 理论上,你可以在没有初始化静态变量的情况下调用构造函数或静态方法 – 但必须在引用静态变量之前初始化它。

初始化期间会发生什么

首先,所有静态变量都接收其默认值(0,null等)。

然后,以文本顺序初始化该类型的静态变量。 如果静态变量的初始化表达式需要初始化另一个类型,那么在分配变量值之前将完全初始化该另一个类型 – 除非第二个类型已经被初始化(由于循环依赖性)。 基本上,类型是:

  • 已经初始化了
  • 正在初始化
  • 没有初始化

仅在未初始化类型时才会触发初始化。 这意味着当存在循环依赖关系时,可以在分配初始值之前观察静态变量的值。 这就是我的Child / Parent示例所显示的内容。

在执行了所有静态变量初始化程序之后,执行静态构造函数。

有关所有这些内容的更多详细信息,请参阅C#规范的第10.12节。


根据大众的需求,当我认为问题是关于中静态变量的初始化顺序时,这是我的原始答案:

根据C#规范的10.5.5.1节,静态变量按文本顺序初始化:

类的静态字段变量初始值设定项对应于以它们出现在类声明中的文本顺序执行的赋值序列。

请注意,部分类型使得这一点变得更加棘手,因为没有一个规范的“文本顺序”。

如果您担心订单,可以随时将代码放在静态构造函数中。 这是我注册我的依赖属性的地方。

不,我认为不可靠不是这里的正确用语。

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

我不知道c ++,但只是在某些情况下,例如在multithreading环境中,如果两种类型试图访问共享资源,如果这是静态的话,则无法确定谁将获胜以及哪一种将正常工作。

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