调用静态构造函数和实例构造函数

时间:2013-03-17 14:57:06

标签: c#

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

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

为什么会出现这种矛盾的行为?

3 个答案:

答案 0 :(得分:19)

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

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

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的实例构造函数的主体。
  • Control返回Child的实例构造函数,并且其正文运行。

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

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

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

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

答案 1 :(得分:6)

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

来自MSDN Doc

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

答案 2 :(得分: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的回答给出了更准确的解释。以上代码仅在静态构造函数的 end 处执行WriteLine。在静态构造函数的开头处添加其他WriteLine,以查看XyzParent静态构造函数是否在XyzChild静态执行的“中间”运行构造