在使用C#应用程序时,我只是注意到在几个地方静态初始化程序彼此依赖,如下所示:
static private Lista = new List () { 0 }; static private List b = new List () { a[0] };
没有做任何有用的特殊工作.这只是运气吗?C#有解决这个问题的规则吗?
编辑:( re:Panos)在一个文件中,词汇顺序似乎是王道?跨文件怎么样?
看起来我尝试了这样的周期性依赖:
static private Lista = new List () { b[0] }; static private List b = new List () { a[0] };
并且该程序没有运行相同(测试套装全面失败,我没有看得更远).
有关规则,请参阅C#规范的10.4节:
初始化类时,首先将该类中的所有静态字段初始化为其默认值,然后以文本顺序执行静态字段初始值设定项.同样,当创建类的实例时,首先将该实例中的所有实例字段初始化为其默认值,然后以文本顺序执行实例字段初始值设定项.具有可变初始值设定项的静态字段可以在其默认值状态下被观察到.然而,作为一种风格问题,强烈建议不要这样做.
换句话说,在您的示例中,'b'被初始化为其默认状态(null),因此在'a'的初始化程序中对它的引用是合法的,但会导致NullReferenceException.
这些规则与Java不同(请参阅 JLS for Java关于前向引用的规则的第8.3.2.3节,这些规则更具限制性).
它似乎取决于线的顺序.此代码有效:
static private Lista = new List () { 1 }; static private List b = new List () { a[0] };
虽然这段代码不起作用(它抛出一个NullReferenceException
)
static private Lista = new List () { b[0] }; static private List b = new List () { 1 };
所以,显然没有关于周期性依赖的规则.然而,奇怪的是编译器没有抱怨......
编辑 - "跨文件"发生了什么?如果我们声明这两个类:
public class A { public static Lista = new List () { B.b[0] }; } public class B { public static List b = new List () { 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).既然a
是null
,b
也不能正确初始化.
如果我们没有周期依赖,一切正常.
编辑:为了防止你没有阅读评论,Jon Skeet提供了一个非常有趣的阅读:静态构造函数和类型初始值设定项之间的区别.