C# C中静态构造函数/初始值设定项的顺序#
在开发C#应用程序时,我注意到在一些地方,静态初始值设定项相互依赖,如下所示:C# C中静态构造函数/初始值设定项的顺序#,c#,static,dependencies,internals,C#,Static,Dependencies,Internals,在开发C#应用程序时,我注意到在一些地方,静态初始值设定项相互依赖,如下所示: static private List<int> a = new List<int>() { 0 }; static private List<int> b = new List<int>() { a[0] }; static private List<int> a = new List<int>() { b[0] }; static priv
static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };
static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { a[0] };
static private List a=new List(){0};
静态私有列表b=新列表(){a[0]};
没有做任何特别有效的事情。这只是运气吗?C#有规则来解决这个问题吗
编辑:(re:Panos)在文件中的词法顺序似乎是最重要的?那么跨文件呢
在寻找过程中,我尝试了如下循环依赖:
static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };
static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { a[0] };
static private List a=new List(){b[0]};
静态私有列表b=新列表(){a[0]};
而且程序也没有运行相同的程序(测试套装全部失败,我没有进一步查看)。这似乎取决于行的顺序。此代码适用于:
static private List<int> a = new List<int>() { 1 };
static private List<int> b = new List<int>() { a[0] };
因此,显然不存在周期性依赖的规则。但奇怪的是,编译器并没有抱怨
编辑-文件之间发生了什么?如果我们声明这两类:
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] };
}
我们将获得以下输出:
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
也无法正确初始化
如果我们没有周期性的依赖关系,那么一切都很好
编辑:为了防止你没有读到评论,提供了一个非常有趣的阅读:。是的,你很幸运。C#显示为按照代码在类中的显示顺序执行代码
static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };
static private List a=new List(){0};
静态私有列表b=新列表(){a[0]};
会有用的,但是
static private List<int> b = new List<int>() { a[0] };
static private List<int> a = new List<int>() { 0 };
static private List b=new List(){a[0]};
静态私有列表a=新列表(){0};
将失败
我建议将所有依赖项放在一个地方,静态构造函数就是这个地方
static MyClass()
{
a = new List<int>() { 0 };
b = new List<int>() { a[0] };
}
静态MyClass()
{
a=新列表(){0};
b=新列表(){a[0]};
}
就我个人而言,我会去掉静态初始值设定项,因为它不清楚,并添加一个静态构造函数来初始化这些变量
static private List<int> a;
static private List<int> b;
static SomeClass()
{
a = new List<int>() { 0 };
b = new List<int>() { a[0] };
}
静态私有列表a;
静态私有列表b;
静态类()
{
a=新列表(){0};
b=新列表(){a[0]};
}
这样你就不必猜测发生了什么,你的意图也就很清楚了。请参阅此处的规则:
初始化类时,该类中的所有静态字段首先初始化为其默认值,然后按文本顺序执行静态字段初始值设定项。类似地,创建类的实例时,该实例中的所有实例字段首先初始化为其默认值,然后按文本顺序执行实例字段初始值设定项。具有变量初始值设定项的静态字段可以在其默认值状态下观察到。然而,这是强烈反对的风格问题
换句话说,在您的示例中,“b”被初始化为其默认状态(null),因此在“a”的初始值设定项中对它的引用是合法的,但会导致NullReferenceException
这些规则与Java的不同(请参阅Java关于转发引用的规则,这些规则更具限制性)。回答了我的问题。然而,我似乎并没有问我想问的问题:跨文件如何?我不是一个C#whiz(只是知道去哪里看),但请注意--“可以构造循环依赖项,允许在默认值状态下观察带有变量初始值设定项的静态字段”。这有用吗?@Cowan不太有用。它没有解决跨文件的问题。我认为跨文件(即跨不同的类)也会发生同样的情况。在类A的类型初始化期间,将要求类B进行初始化,类B将找到对类A的空引用。现在,在同一类(部分类)的文件中,可能由预处理器来确定它是否失败。因此,如果A引用B.B,则A.A被初始化将碰撞B.B?这是肯定的。第一次引用类型时会调用静态构造函数。这里要注意静态变量初始值设定项和静态构造函数之间的区别。基于静态构造函数的存在/不存在,在类型初始化发生时有不同的规则。请注意,这些代码在运行时并不完全相同: