调用静态构造函数和实例构造函数
本文关键字:构造函数 实例 调用 静态 | 更新日期: 2023-09-27 18:04:14
据我所知,父类的构造函数首先被调用,然后是子类。但是为什么在静态构造函数的情况下,它执行从派生类,然后子类?
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建议,当我在构造函数中初始化静态字段时,它按以下顺序运行
父静态构造函数
子静态构造函数
父实例构造函数
子实例构造函数
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");
}
}
为什么会有这样矛盾的行为?
首先,这种行为根本不矛盾;一切都符合规定。你只是不知道规则是什么。
您应该阅读我关于实例构造函数的两部分系列文章和关于静态构造函数语义的四部分系列文章。它们从这里开始:
http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx:
http://ericlippert.com/2013/02/06/static-constructors-part-one/。
这些应该清楚地回答了你的问题,但如果不是100%清楚,让我总结一下。相关规则为:- 规则一:静态构造函数在任何静态字段被访问之前,在任何静态方法被执行之前,在任何实例构造函数被执行之前运行。
- 规则二:派生类实例构造函数在运行派生类实例构造函数体之前调用基类实例构造函数。
那么当执行new Child()
时会发生什么呢?
- 规则一适用。我们将调用Child的实例构造函数,因此必须首先调用Child的静态构造函数。所以它先运行。
- Child的静态构造函数返回后,Child的实例构造函数运行。规则二适用:子实例构造函数在运行它的主体之前做的第一件事就是运行Parent的实例构造函数。
- 规则一再次适用。我们将要调用Parent的实例构造函数,因此必须首先调用Parent的静态构造函数。所以它运行。
- Parent的静态构造函数返回后,Parent的实例构造函数运行。规则二适用:它调用object的实例构造函数,它没有做什么有趣的事情,然后运行Parent的实例构造函数的主体。
- 控制返回到Child的实例构造函数,并且运行Child的主体。
好了;顺序是Child静态构造函数、Parent静态构造函数、Parent主体、Child主体。
现在让我们看看第二个例子。当你说new XyzChild
时会发生什么?
- 规则一适用。我们要调用XyzChild的实例构造函数,所以我们首先调用XyzChild的静态构造函数。它的主体开始执行,然后…
- …规则一再次适用。我们即将访问XyzParent的静态字段,因此必须执行XyzParent的静态构造函数。 执行XyzParent的静态构造函数。它访问一个字段,但是静态构造函数已经在这个线程上运行,所以它不会再次递归地触发静态构造函数。它打印出它在父进程中。
- 控件返回到子进程的静态构造函数,该构造函数打印出控件在子进程中。现在子线程的实例构造函数可以运行了。规则二:XyzParent的实例构造函数首先运行。
- 规则1适用,但是XyzParent的静态构造函数已经运行,所以它被跳过。
- XyzParent的实例构造函数执行并返回XyzChild的静态构造函数的控制。
- XyzChild的实例构造函数运行。
就是这样。没有任何矛盾;这两条规则得到了正确的应用。
Static
构造函数总是在非静态构造函数之前执行。静态构造函数在类第一次被访问时被调用。
- 静态构造函数
From MSDN Doc,
- 静态构造函数不接受访问修饰符,也没有参数。
- 在创建第一个实例或引用任何静态成员之前,自动调用静态构造函数来初始化类。静态构造函数不能直接调用。用户无法控制静态构造函数何时在程序中执行。 静态构造函数的典型用法是当类使用日志文件时,构造函数用于向该文件写入条目。静态构造函数在为非托管代码创建包装类时也很有用,因为构造函数可以调用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");
}
}
现在更重要的是它们运行的顺序,因为它们写的是同一个字段。在我的代码版本中,输入new XyzChild();
会导致以下输出:
parent static
child static
parent instance
child instance
编辑:Eric Lippert的回答给出了更精确的解释。上面的代码只在静态构造函数的结尾处执行WriteLine
。在静态构造函数的开头添加额外的WriteLine
,以查看XyzParent
静态构造函数在执行XyzChild
静态构造函数的"中间"运行。