6

据我所知,首先调用父类的构造函数,然后调用子类。但是为什么在静态构造函数的情况下,它首先从派生类执行,然后是子类?

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");
    }
}

为什么会有这种矛盾的行为?

4

3 回答 3

24

首先,行为根本不矛盾;这一切都符合规则。你只是不知道规则是什么。

您应该阅读我关于实例构造函数的所有两部分系列文章和关于静态构造函数语义的四部分系列文章。他们从这里开始:

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的实例构造函数运行。规则二适用:Child 实例构造函数在运行其主体之前所做的第一件事是运行 Parent 的实例构造函数。
  • 规则一再次适用。我们即将调用Parent的实例构造函数,所以必须先调用Parent的静态构造函数。所以它运行。
  • Parent的静态构造函数返回后,Parent的实例构造函数运行。规则二适用:它调用 object 的实例构造函数,它没有做任何有趣的事情,然后运行 ​​Parent 的实例构造函数的主体。
  • 控制返回到 Child 的实例构造函数,并且它的主体运行。

所以你去; 顺序是 Child 静态构造函数,然后是 Parent 静态构造函数,然后是 Parent 主体,然后是 Child 主体。

现在让我们看看你的第二个例子。当你说的时候会发生什么new XyzChild

  • 规则一适用。我们即将调用XyzChild的实例构造函数,所以我们先调用XyzChild的静态构造函数。它的身体开始执行,然后……
  • ...规则一再次适用。我们即将访问 XyzParent 的一个静态字段,所以必须执行 XyzParent 的静态构造函数。
  • XyzParent 的静态构造函数执行。它访问一个字段,但静态构造函数已经在该线程上运行,因此它不会再次递归触发静态构造函数。它打印出它在父级中。
  • 控制权返回给子对象的静态构造函数,该构造函数打印出它在子对象中。
  • 现在孩子的实例构造函数可以运行了。规则二适用:XyzParent 的实例构造函数首先运行。
  • 规则一适用,但 XyzParent 的静态构造函数已经运行,因此被跳过。
  • XyzParent 的实例构造函数的主体执行并将控制权返回给 XyzChild 的静态构造函数。
  • XyzChild 的实例构造函数的主体运行。

所以你去。没有任何不一致之处;正确应用了这两个规则。

于 2013-03-17T20:16:44.700 回答
6

Static构造函数总是在非静态构造函数之前执行。第一次访问类时调用静态构造函数。

来自MSDN 文档

  • 静态构造函数不采用访问修饰符或具有参数。
  • 在创建第一个实例或引用任何静态成员之前,会自动调用静态构造函数来初始化类。
  • 不能直接调用静态构造函数。用户无法控制何时在程序中执行静态构造函数。
  • 静态构造函数的典型用途是当类使用日志文件并且构造函数用于将条目写入该文件时。
  • 在为非托管代码创建包装类时,静态构造函数也很有用,此时构造函数可以调用 LoadLibrary 方法。
  • 如果静态构造函数抛出异常,运行时将不会再次调用它,并且该类型将在程序运行的应用程序域的生命周期内保持未初始化状态。
于 2013-03-17T14:58:36.043 回答
2

在您的情况下,运行静态构造函数的顺序是未定义的(我认为)。唯一可以保证的是它们将在创建实例之前运行。

我将您的代码更改为:

    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

于 2013-03-17T15:23:58.347 回答