在使用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] };
并且该程序运行不同(测试套装全面失败,我没有进一步观察)。
答案 0 :(得分:16)
有关规则,请参阅section 10.4 of the C# spec:
初始化类时,首先将该类中的所有静态字段初始化为其默认值,然后以文本顺序执行静态字段初始值设定项。同样,当创建类的实例时,首先将该实例中的所有实例字段初始化为其默认值,然后以文本顺序执行实例字段初始值设定项。具有可变初始值设定项的静态字段可以在其默认值状态下被观察到。但是,作为一种风格问题,强烈建议不要这样做。
换句话说,在您的示例中,'b'被初始化为其默认状态(null),因此在'a'的初始值设定项中对它的引用是合法的,但会导致NullReferenceException。
这些规则与Java不同(请参阅section 8.3.2.3 of the JLS了解有关前向引用的Java规则,这些规则更具限制性。)
答案 1 :(得分:14)
似乎取决于线的顺序。此代码有效:
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
和lefts字段a
中的异常具有默认值(null)。由于a
为null
,因此b
也无法正确初始化。
如果我们没有周期性依赖,那么一切正常。
编辑:如果您没有阅读评论,Jon Skeet提供了一个非常有趣的阅读:The differences between static constructors and type initializers。
答案 2 :(得分:2)
我个人会删除静态初始化器,因为它不清楚并添加一个静态构造函数来初始化这些变量。
static private List<int> a;
static private List<int> b;
static SomeClass()
{
a = new List<int>() { 0 };
b = new List<int>() { a[0] };
}
然后你不必猜测发生了什么,你的意图是明确的。
答案 3 :(得分:0)
是的,你很幸运。 C#似乎按照它在类中出现的顺序执行代码。
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] };
}