C# 中静态构造函数/初始化程序的顺序

发布于 2024-07-07 04:04:06 字数 546 浏览 4 评论 0原文

在开发 C# 应用程序时,我注意到在几个地方静态初始化程序相互依赖,如下所示:

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };

无需执行任何有效的特殊操作。 这只是运气吗? C# 有解决这个问题的规则吗?

编辑:(回复:Panos)在文件中词汇顺序似乎才是王道? 跨文件怎么样?

在查看时,我尝试了这样的循环依赖:

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { a[0] };

并且程序运行不一样(测试套件全面失败,我没有进一步查看)。

While working on a C# app I just noticed that in several places static initializers have dependencies on each other like this:

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };

Without doing anything special that worked. Is that just luck? Does C# have rules to resolve this?

Edit: (re: Panos) In a file lexical order seems to be king? what about across files?

In looking I tried a cyclical dependency like this:

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { a[0] };

and the program didn't run the same (the test suit failed across the board and I didn't look further).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

晨曦慕雪 2024-07-14 04:04:06

有关规则,请参阅 C# 规范第 10.4 节这里:

当一个类被初始化时,该类中的所有静态字段首先被初始化为其默认值,然后静态字段初始值设定项按文本顺序执行。 同样,当创建类的实例时,该实例中的所有实例字段首先被初始化为其默认值,然后实例字段初始值设定项按文本顺序执行。 具有变量初始值设定项的静态字段可以在其默认值状态下被观察到。 然而,出于风格问题,强烈建议不要这样做。

换句话说,在您的示例中,“b”被初始化为其默认状态(null),因此“a”的初始化程序中对它的引用是合法的,但会导致 NullReferenceException。

这些规则与 Java 的规则不同(请参阅 第 8.3.2.3 节Java 关于前向引用的规则的 JLS,其限制性更强)。

See section 10.4 of the C# spec for the rules here:

when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order. Likewise, when an instance of a class is created, all instance fields in that instance are first initialized to their default values, and then the instance field initializers are executed in textual order. It is possible for static fields with variable initializers to be observed in their default value state. However, this is strongly discouraged as a matter of style.

So in other words, in your example 'b' is initialized to its default state (null) and so the reference to it in the initializer of 'a' is legal but would result in a NullReferenceException.

These rules are different to Java's (see section 8.3.2.3 of the JLS for Java's rules about forward references, which are more restrictive).

舂唻埖巳落 2024-07-14 04:04:06

这似乎取决于行的顺序。 这段代码有效:

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)。 由于anull,因此b也无法正确初始化。

如果我们没有循环依赖,一切都会正常进行。


编辑:以防万一您没有阅读评论,Jon Skeet 提供了非常有趣的阅读:静态构造函数和类型初始值设定项之间的区别

It seems to depend on the sequence of lines. This code works:

static private List<int> a = new List<int>() { 1 };
static private List<int> b = new List<int>() { a[0] };

while this code does not work (it throws a NullReferenceException)

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { 1 };

So, obviously no rules for cyclical dependency exist. It's peculiar however that the compiler does not complain...


EDIT - What's happening "across files"? If we declare these two classes:

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] };
}

and try to access them with this code:

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); }

we are getting this output:

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.

So the initialization of B causes an exception in static constructor A and lefts field a with the default value (null). Since a is null, b can not also be initialized properly.

If we do not have cyclical dependencies, everything works fine.


EDIT: Just in case you didn't read the comments, Jon Skeet provides a very interesting reading: The differences between static constructors and type initializers.

花期渐远 2024-07-14 04:04:06

就我个人而言,我会摆脱静态初始化程序,因为它不清楚,并添加静态构造函数来初始化这些变量。

static private List<int> a;
static private List<int> b;

static SomeClass()
{
    a = new List<int>() { 0 };
    b = new List<int>() { a[0] };
}

这样你就不必猜测发生了什么,你的意图就很清楚了。

Personally I would get rid of the static initializers since it isn't clear and add a static constructor to initialize these variables.

static private List<int> a;
static private List<int> b;

static SomeClass()
{
    a = new List<int>() { 0 };
    b = new List<int>() { a[0] };
}

Then you don't have to guess what is going on and you're being clear in your intentions.

┾廆蒐ゝ 2024-07-14 04:04:06

是的,你很幸运。 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] };
}

Yes, you were lucky. C# appears to execute the code in the order it appears in the class.

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };

Will work but ...

static private List<int> b = new List<int>() { a[0] };
static private List<int> a = new List<int>() { 0 };

Will fail.

I would recommend putting all your dependencies in one place, the static constructor is the place for this.

static MyClass()
{
  a = new List<int>() { 0 };
  b = new List<int>() { a[0] };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文