C#中静态构造函数/初始化器的顺序
本文关键字:顺序 初始化 静态 构造函数 | 更新日期: 2023-09-27 17:47:49
在开发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] };
程序运行不一样(测试服全面失败,我没有看得更远)。
关于这里的规则,请参见C#规范的10.4节:
初始化一个类时,该类中的所有静态字段首先初始化为默认值,然后按文本顺序执行静态字段初始化程序。同样,当创建一个类的实例时,该实例中的所有实例字段首先初始化为其默认值,然后按文本顺序执行实例字段初始化程序。具有变量初始值设定项的静态字段可以在其默认值状态下观察到。然而,作为一种风格问题,我们强烈反对这样做。
换句话说,在您的示例中,"b"被初始化为其默认状态(null),因此在"a"的初始化器中对它的引用是合法的,但会导致NullReferenceException。
这些规则与Java的规则不同(关于Java关于前向引用的规则,请参见JLS的第8.3.2.3节,后者的限制性更强)。
这似乎取决于行的顺序。此代码有效:
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
中引起异常,并将字段a
保留为默认值(null)。由于a
是null
,因此b
也不能被正确地初始化。
如果我们没有周期性的依赖关系,一切都会很好。
编辑:Jon Skeet提供了一篇非常有趣的文章:静态构造函数和类型初始化器之间的区别,以防你没有阅读评论。
就我个人而言,我会去掉静态初始化器,因为它不清楚,并添加一个静态构造函数来初始化这些变量。
static private List<int> a;
static private List<int> b;
static SomeClass()
{
a = new List<int>() { 0 };
b = new List<int>() { a[0] };
}
然后你就不必猜测发生了什么,而且你的意图很清楚。
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] };
}