C# 调用静态构造函数和实例构造函数
据我所知,父类的构造函数首先被调用,然后是子类。但为什么在静态构造函数的情况下,它首先从派生类执行,然后是子类C# 调用静态构造函数和实例构造函数,c#,C#,据我所知,父类的构造函数首先被调用,然后是子类。但为什么在静态构造函数的情况下,它首先从派生类执行,然后是子类 namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Child t = new Child(); } } class Parent {
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Child t = new Child();
}
}
class Parent
{
public Parent()
{
Console.WriteLine("Parent Instance Constructor");
Console.ReadKey();
}
static Parent()
{
Console.WriteLine("Parent Static Constructor");
Console.ReadKey();
}
}
class Child : Parent
{
public Child()
{
Console.WriteLine("Child Instance Constructor");
Console.ReadKey();
}
static Child()
{
Console.WriteLine("Child Static Constructor");
Console.ReadKey();
}
}
}
输出:
子静态构造函数
父静态构造函数
父实例构造函数
子实例构造函数
现在,根据Jeppe Stig Nielsen的建议,当我初始化构造函数中的静态字段时,它按以下顺序运行
输出
父静态构造函数
子静态构造函数
父实例构造函数
子实例构造函数
为什么会有如此矛盾的行为?静态的构造函数总是在非静态构造函数之前执行。第一次访问类时调用静态构造函数
- 静态构造函数不接受访问修饰符或具有参数
- 在创建第一个实例或引用任何静态成员之前,会自动调用静态构造函数来初始化类
- 不能直接调用静态构造函数。 用户无法控制在程序中何时执行静态构造函数
- 静态构造函数的一个典型用法是当类使用日志文件并且构造函数用于向该文件写入条目时
- 当构造函数可以调用LoadLibrary方法时,静态构造函数在为非托管代码创建包装类时也很有用
- 如果静态构造函数抛出异常,运行时将不会再次调用它,并且在程序运行的应用程序域的生命周期内,该类型将保持未初始化状态
class XyzParent
{
protected static int FieldOne;
protected int FieldTwo;
static XyzParent()
{
FieldOne = 1;
Console.WriteLine("parent static");
}
internal XyzParent()
{
FieldOne = 10;
FieldTwo = 20;
Console.WriteLine("parent instance");
}
}
class XyzChild : XyzParent
{
static XyzChild()
{
FieldOne = 100;
Console.WriteLine("child static");
}
internal XyzChild()
{
FieldOne = 1000;
FieldTwo = 2000;
Console.WriteLine("child instance");
}
}
现在更重要的是它们运行的顺序,因为它们写入同一个字段。用我的代码版本,说newxyzchild()代码>导致此输出:
parent static
child static
parent instance
child instance
编辑:Eric Lippert的回答给出了更精确的解释。上述代码仅在静态构造函数的末尾写入线。在静态构造函数的开头添加额外的WriteLine
,以查看XyzParent
静态构造函数是在执行XyzChild
静态构造函数的“中间”运行的。首先,行为一点也不矛盾;这一切都符合规则。你只是不知道规则是什么
您应该阅读我的关于实例构造函数的两部分系列文章和关于静态构造函数语义的四部分系列文章。他们从这里开始:
在这里:
分别
这些应该清楚地回答你的问题,但如果不是100%清楚,让我总结一下。有关规则如下:
- 规则一:静态构造函数在访问任何静态字段、执行任何静态方法和执行任何实例构造函数之前运行
- 规则二:派生类实例构造函数在运行派生类实例构造函数体之前调用基类实例构造函数
那么,当您执行newchild()
时会发生什么呢
- 规则一适用。我们即将调用Child的实例构造函数,因此必须首先调用Child的静态构造函数。所以它首先运行
- 子类的静态构造函数返回后,子类的实例构造函数运行。规则二适用:子实例构造函数在运行其主体之前做的第一件事是运行父实例构造函数
- 第一条规则同样适用。我们将要调用父对象的实例构造函数,因此必须首先调用父对象的静态构造函数。所以它运行
- 父级的静态构造函数返回后,父级的实例构造函数运行。规则二适用:它调用object的实例构造函数,而object没有做任何有趣的事情,然后运行父实例构造函数的主体
- 控件返回子控件的实例构造函数,并运行其主体
你就这样走了;顺序是子静态构造函数,然后是父静态构造函数,然后是父主体,然后是子主体
现在让我们来看第二个例子。当你说new XyzChild
时会发生什么
- 规则一适用。我们将要调用XyzChild的实例构造函数,因此我们首先调用XyzChild的静态构造函数。它的身体开始执行,并且
- …规则一再次适用。我们即将访问XyzParent的静态字段,因此必须执行XyzParent的静态构造函数李>
- 执行XyzParent的静态构造函数。它访问一个字段,但静态构造函数已经在此线程上运行,因此它不会再次递归触发静态构造函数。它打印出它在父对象中
- 控件返回到子级的静态构造函数,该构造函数打印出它在子级中
- 现在,子实例构造函数可以运行了。规则二适用:XyzParent的实例构造函数首先运行
- 规则一适用,但XyzParent的静态构造函数已经运行,因此跳过它
- XyzParent的实例构造函数的主体执行并将控制权返回给XyzChild的静态构造函数
- XyzChild实例构造函数的主体将运行
好了。没有任何矛盾;这两条规则应用正确 如果您给类Parent
两个字段,一个static
一个和一个实例字段,那么会更有趣。然后构造函数应该分配
parent static
child static
parent instance
child instance