在调用构造函数之前是否应该完成静态字段初始化?
以下程序提供的输出对我来说似乎不正确。
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();
}
}
答案 0 :(得分:25)
这是正确的。
您的静态初始化器,然后静态构造函数在标准构造函数之前运行,但是当它运行时,它使用新的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(); }
。这将打印“静态A()”消息。
最后,您的原始语句(var a = new A();
)已执行,但此时,构建了静态,因此您可以获得最终的打印。
答案 1 :(得分:3)
一个额外的注意事项 - C#规范(我正在看4.0,但它也在3.0中)在10.5.5.1静态字段初始化中说:
如果是静态构造函数(§10.12) 存在于类中,执行 发生静态字段初始化程序 在执行之前 静态构造函数。否则, 执行静态字段初始值设定项 在依赖于实现的时间 在第一次使用静电之前 该班的领域。
您有一个静态构造函数,因此“其他”子句不适用。但我认为,如果您没有静态构造函数,那么您的问题就是相关信息,静态字段初始化程序可以在“依赖于实现的时间”执行。如果静态字段初始化程序正在执行某种类型的数据初始化或对象创建,而不依赖于静态字段本身,则这可能很重要。
我猜,这是深奥的,但我看到它发生在今天,因为'实现依赖时间'似乎在C#3.0和4.0之间发生了变化 - 至少对于我正在看的情况而言。简单的解决方案当然很简单 - 只需添加一个静态构造函数......
答案 2 :(得分:1)
我实际上相信它正在按照你的想法行事。你的测试很难说清楚。
您对_A
的初始化public static string _A = (new A()).I();
首先创建一个新的A实例,因此你的新A()和_A = null的着作。因为它在启动时为null,因为这是初始化。一旦初始化,就会调用静态构造函数,它将返回新实例。
答案 3 :(得分:0)
似乎编译器正在做预期的事情。
1st - 在类中执行所有静态代码(首先是字段,然后是静态构造函数):
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);
}
你问为什么这是可能的。嗯,在我看来,一个实例并不绝对要求在创建时初始化所有类变量。它只需要它们必须存在。我认为这个特殊情况支持这种想法,因为在完成所有静态初始化之前就会创建一个实例。
答案 4 :(得分:-1)
是的,静态字段初始化应该在调用构造函数之前完成。但是你把编译器置于异常情况下它就不能遵守这个规则。
这是一个有趣的技巧,但在正常的应用中不会发生。