DependencyProperty.AddOwner MSDN page提供了一个带有静态成员的两个类的示例,一个类的成员依赖于另一个类的成员进行初始化。我认为MSDN是错误的 - 静态变量的初始化顺序在C#just like it is in C++或其他任何地方都是不可靠的。我可能错了,因为WPF库本身就是这样编写的,它运行得很好。我错过了什么? C#编译器如何知道安全初始化顺序?
答案 0 :(得分:26)
只要你没有在一个循环中结束,一种类型可以依赖于另一种被初始化的类型。
基本上这很好:
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=0
,Parent.Y=10
,Child.X=10
或 Child.Nasty=0
,Parent.Y=0
,Child.X=10
取决于首先访问的类。
访问Parent.Y first
将首先开始初始化Parent
,这会触发Child
的初始化。 Child
的初始化将意识到Parent
需要初始化,但CLR知道它已经被初始化,因此无论如何都要继续,导致第一组数字 - 因为Child.X
最终在其值用于Parent.Y
之前进行初始化。
访问Child.Nasty
将首先开始初始化Child
,然后开始初始化Parent
。 Parent
的初始化将意识到Child
需要初始化,但CLR知道它已经被初始化,因此无论如何都会继续,导致第二组数字。
不要这样做。
编辑:好的,更详细的解释,如承诺的那样。
何时初始化类型?
如果类型具有静态构造函数,则只会初始化它 当它第一次使用时(无论是引用静态成员,还是 创建实例时)。如果没有有静态 构造函数,它可以在之前初始化。从理论上讲,它也可以 稍后初始化;你理论上可以称之为构造函数或 没有静态变量初始化的静态方法 - 但是 必须在引用静态变量之前初始化。
初始化期间会发生什么?
首先,所有静态变量都接收默认值(0,null 等)。
然后,在文本中初始化该类型的静态变量 订购。如果静态变量的初始化表达式需要 另一种要初始化的类型,那么其他类型将是 在赋值变量之前完全初始化 - 除非第二种类型已经被初始化(由于a 循环依赖)。基本上,类型是:
只有在未初始化类型时才会触发初始化。
这意味着当存在循环依赖性时,它是可能的
在初始值之前观察静态变量的值
被分配。这就是我的Child
/ Parent
示例所示。
执行完所有静态变量初始化程序后,静态 构造函数执行。
有关所有这些内容的更多详细信息,请参阅C#规范的第10.12节。
根据大众需求,当我认为问题是关于类中静态变量的初始化顺序时,这是我的原始答案:
根据C#规范的第10.5.5.1节
,按文本顺序初始化静态变量静态字段变量初始值设定项 一个类对应于一个序列 在。中执行的作业 它们出现的文本顺序 班级宣言。
请注意,部分类型会使这一点变得更加棘手,因为该类没有任何规范的“文本顺序”。
答案 1 :(得分:0)
如果您担心订单,可以随时将代码放在静态构造函数中。这是我注册我的依赖属性的地方。
答案 2 :(得分:0)
不,我认为不可靠不是这里的正确用语。
在真正的单线程场景中,当在代码中首次访问该类型的任何静态成员时,将初始化类的静态成员。
我不知道c ++,但仅在某些情况下,例如在多线程环境中,如果有两种类型试图访问共享资源,如果这是静态的,则无法确定谁将获胜以及哪一种将正常工作。< / p>
MSDN示例是正确的,并且可以正常工作。