English 中文(简体)
C# 中的静态字段初始化是如何工作的?
原标题:
  • 时间:2009-04-02 17:38:03
  •  标签:

静态字段初始化应在构造函数调用之前完成吗?

以下程式提供的輸出似乎對我來說不正確。

new A()
_A == null
static A()
new A()
_A == A

代码:

public class A
{
    public static string _A = (new A()).I();

    public A()
    {
        Console.WriteLine("new A()");
        if (_A == null)
            Console.WriteLine("_A == null");
        else
            Console.WriteLine("_A == " + _A);
    }

    static A()
    {
        Console.WriteLine("static A()");
    }

    public string I()
    {
        return "A";
    }
}

class Program
{
    static void Main(string[] args)
    {
       var a = new A();
    }
}
最佳回答

这是正确的。

你的静态初始化程序和静态构造函数会在标准构造函数之前运行,但它运行时,它会使用new A(),因此通过非静态构造函数路径。这引起了你看到的信息。

这是完整的执行路径:

当您在程序中首次调用var a = new A();时,这是访问A的第一次。

这将触发 A._A 的静态初始化。

在这一点上,A._A 用 _A = (new A()).I(); 进行构建。

这是一次打击。


Console.WriteLine("new A()");
if (_A == null)
    Console.WriteLine("_A == null");        

自此时起,_A尚未使用返回的构造类型进行设置。

接下来运行静态构造函数 A { static A(); }。这将打印出“static A()” 的消息。

最终,执行您的原始语句 (var a = new A();) ,但此时静态成员已构建完成,因此您将得到最终打印。

问题回答

附加侧记 - C#规范(我正在看4.0,但它也存在于3.0中)在10.5.5.1静态字段初始化中表示:

If a static constructor (§10.12) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.

您有一个静态构造函数,所以“否则”条款不适用。但是我认为,了解以下信息对您的问题很重要:如果您没有静态构造函数,则静态字段初始化器可以在实现相关的时间执行。如果您的静态字段初始化器正在执行某种类型的数据初始化或对象创建,而您正在依赖它而不访问静态字段本身,则这可能很重要。

我猜这有点玄学,但我今天看到了,因为在C#3.0和4.0之间,实现依赖的时间似乎发生了变化 - 至少对于我正在查看的情况是如此。当然,简单的解决方案很简单 - 只需添加一个静态构造函数...

我实际上相信它正在做你认为的事情。你的测试让人难以辨别。

你的_A的初始化

public static string _A = (new A()).I();

首先创建一个新的A实例,因此您写的是new A()和_A = null。因为它在开始时是null,所以这是初始化。一旦初始化,静态构造函数就会被调用,它返回新的实例。

似乎编译器正在做预期的工作。

第一步-在类中执行所有静态代码(先是字段,然后是静态构造函数):

public static string _A = (new A()).I();

// and

static A()
{
    Console.WriteLine("static A()");
}

第二 - 类构造函数被调用:

public A()
{
    Console.WriteLine("new A()");
    if (_A == null)
        Console.WriteLine("_A == null");
    else
        Console.WriteLine("_A == " + _A);
}

你问为什么这是可能的。嗯,在我看来,一个实例不一定要求在创建时初始化所有类变量,它只需要存在就可以了。我认为这种特殊情况支持了这种想法,因为一个实例是在所有静态初始化完成之前被创建的。

是的,在调用构造函数之前,静态字段的初始化应该完成。但是你把编译器置于不正常的情况下,它无法遵守这个规则。

这是一个有趣的技巧,但它不会在正常应用中发生。





相关问题
热门标签