字段初始化器(静态或非静态)和构造函数(静态或非静态),哪个会先运行?

5

有些地方不太清楚,根据我所读的:

  1. 字段初始化程序 在构造函数之前运行。
  2. 静态字段初始化程序 在调用 静态构造函数 之前执行(仍然符合第一点)。
  3. 如果类型没有静态构造函数,则会在使用该类型之前(理解为被使用而非实例化)执行 字段初始化程序

以下示例进行了说明:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Foo.X);
        Console.ReadLine();
    }
}

class Foo
{
    public static Foo Instance = new Foo();
    public static int X = 3;

    Foo()
    {
        Console.WriteLine("In constructor: " + X);
    }
}

这段代码先打印0,然后是3!可能吗?当我们通过Foo.X使用Foo时,前两个初始化器会在构造函数之前被调用(到目前为止还好),当...

public static Foo Instance = new Foo();

在执行时,它应该在调用构造函数之前运行自己的两个初始化程序(第一点),而实际上它首先运行构造函数并以0作为默认值打印X。

我真的无法理解这个逻辑,请向我澄清。

编辑:我的预期结果是:

  1. Foo.X:执行:public static Foo Instance = new Foo();
  2. 在调用打印(“在构造函数中:”+ X)的构造函数之前,应该先执行public static int X = 3;,但事实上构造函数首先触发了。难道不应该首先完成字段的运行吗?我的意思是即使在跳转到创建新的Foo实例时,字段也必须先运行。
  3. 根据最后两点,我希望先打印3然后再打印3。
2个回答

5
如果一个类型没有静态构造函数,则在使用该类型(我理解为未实例化但被使用)之前将执行字段初始化程序。
不一定如此。
如果没有静态构造函数,则静态字段初始化程序将在第一次使用静态字段之前的某个时间执行,但是静态字段初始化程序不需要在创建任何实例之前执行。
来自C# 5规范第10.5.5.1节:
类的静态字段变量初始值设定项对应于按照它们在类声明中出现的文本顺序执行的一系列赋值。如果类中存在静态构造函数(§10.12),则在执行该静态构造函数之前立即执行静态字段初始化程序。否则,在使用该类的静态字段之前,在实施依赖性的时间执行静态字段初始化程序。
但在你的情况下,当调用Foo的构造函数以初始化Instance时,X仍为0,因为它还没有被赋值。字段初始化器按文本顺序执行,所以InstanceX之前被赋值。就这么简单——这不是静态字段和实例字段之间的时间问题,因为你没有任何实例字段。
编辑:看起来你被构造函数调用弄混了。Foo已经被初始化——构造函数的调用不会改变这一点,也没有“第二次初始化”。构造函数正常调用,打印“0”,然后返回。然后X被赋值为3。

我知道字段初始化器按文本顺序执行,因此在X之前Instance被赋值,问题是当它被赋值时(也就是构造函数被调用时),它是在将3分配给X之前完成的,这怎么可能呢?(点1.) - AymenDaoudi
@AymenDaoudi:它不会 - 它会打印0。只有在所有静态初始化程序执行后,您才会在打印Foo.X之后打印3。#为了使其更清晰,请将构造函数中的代码更改为:Console.WriteLine("In constructor: " + X);。不太清楚您希望发生什么,但是当您在Foo类型初始化程序内调用Foo的构造函数时,类型初始化程序已经在运行,因此不会单独运行。 - Jon Skeet
如果我理解得比较慢,很抱歉。我已经更新了问题并解释了我的期望。 - AymenDaoudi
@AymenDaoudi:你说过你期望在构造函数执行之前执行X = 3,但没有解释为什么。对于Instance的赋值无法完成直到构造函数完成,而这发生在X = 3的赋值之前。请注意这里的两个字段都是静态字段 - 你所看到的只是静态字段初始化程序调用构造函数的副作用,从而允许另一个静态字段的默认值被看到。 - Jon Skeet
非常感谢Jon,我现在明白了。 - AymenDaoudi

0

Foo() 构造函数不是静态构造函数,因此它不像您的 case 3 那样首先运行。

因此,字段从上到下首先进行初始化。

当首次初始化 static Foo 时,它被执行并打印出 0,然后在初始化 X 时,使用 System.Console.WriteLine() 调用打印出来。


问题在于,当调用Foo.X时,它是自上而下的。当自上而下进行时,它遇到public static Foo Instance = new Foo();如果不是这样,它永远不会打印0,可能是我没有真正理解它。 - AymenDaoudi
它是从上到下执行的,初始化static Foo Instance会调用构造函数,并且请记住,在遇到static int X之前,X仍然为0。然后,在初始化Instance之后,它还将X的值分配为3。然后将其打印到控制台。@AymenDaoudi - Aniket Inge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接