次の方法で共有


Why Do Initializers Run In The Opposite Order As Constructors? Part One

Pop quiz!

What do you expect the output of this program to be?

 

using System;

class Foo
{
public Foo(string s)
{
Console.WriteLine("Foo constructor: {0}", s);
}
public void Bar() { }
}
class Base
{
readonly Foo baseFoo = new Foo("Base initializer");
public Base()
{
Console.WriteLine("Base constructor");
}
}
class Derived : Base
{
readonly Foo derivedFoo = new Foo("Derived initializer");
public Derived()
{
Console.WriteLine("Derived constructor");
}
}
static class Program
{
static void Main()
{
new Derived();
}
}

I got a question from a user recently noting that the order was not as he expected. One naively expects that the order will go "base initializers, base constructor body, derived initializers, derived constructor body". In fact the order actually is that first all the initializers run in order from derived to base, and then all the constructor bodies run in order from base to derived.

The latter bit makes perfect sense; the more derived constructors may rely upon state initialized by the less derived constructors, so the constructors should run in order from base to derived. But most people assume that the call sequence of the code above is equivalent to this pseudocode:

// Expected
BaseConstructor()
{
ObjectConstructor();
baseFoo = new Foo("Base initializer");
Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
BaseConstructor();
derivedFoo = new Foo("Derived initializer");
Console.WriteLine("Derived constructor");
}

When in point of fact it is equivalent to this:

 // Actual
BaseConstructor()
{
baseFoo = new Foo("Base initializer");
ObjectConstructor();
Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
derivedFoo = new Foo("Derived initializer");
BaseConstructor();
Console.WriteLine("Derived constructor");
}

That explains the mechanism whereby the initializers run in order from derived to base and the constructor bodies run in the opposite order, but why did we choose to implement that mechanism instead of the more intuitively obvious former way?

Puzzle that one over for a bit, and then read on for a hint.

...

...

...

The "readonly" modifiers in there were no accident. The code gives the appearance that any call to derivedFoo.Bar() and baseFoo.Bar() should never fail with a null dereference exception because both are readonly fields initialized to non-null values.

  1. Is that appearance accurate, or misleading?
  2. Now suppose initializers ran in the "expected" order and answer question (1) again. 

I'll post the answers and analysis next week. Have a fabulous weekend!

Comments

  • Anonymous
    February 15, 2008
    Possible spoilers by implication, so ROT-13. V'ir nyjnlf yvxrq vg gur jnl vg vf - orpnhfr V'ir rkcrevraprq gur cnva bs vg orvat gur bgure jnl ebhaq. Gur rdhvinyrag (va grezf bs fbhepr) Wnin cebtenz jevgrf: Sbb pbafgehpgbe: Onfr vavgvnyvmre Onfr pbafgehpgbe Sbb pbafgehpgbe: Qrevirq vavgvnyvmre Qrevirq pbafgehpgbe Guvf orunivbhe pnhfrq n irel uneq-gb-qvntabfr reebe va n pbyyrnthr'f pbqr znal zbbaf ntb. Vg graqf abg gb znggre vs lbh qba'g znxr iveghny pnyyf jvguva gur onfr pbafgehpgbe, ohg gung'f rknpgyl jung gur zbfg angheny qrfvta pnyyrq sbe va guvf pnfr. (Gung jnf jura V svefg yrnearq gung vg'f n onq vqrn gb znxr iveghny pnyyf sebz n pbafgehpgbe!)

  • Anonymous
    February 15, 2008
    The most obvious thing that comes to my mind is virtual method calls in ancestor constructors. Bad practice in any case, though sometimes it's the most natural solution.

  • Anonymous
    February 16, 2008
    The comment has been removed

  • Anonymous
    February 16, 2008
    Ah, yes. I should have mentioned that. The rules for static constructors and static fields are subtly different, and I do not intend this article to discuss anything about the rules for statics. That's a great topic for a follow-up article.

  • Anonymous
    February 17, 2008

  1. Accurate.
  2. Misleading. [Waves to James Gosling.]
  • Anonymous
    February 18, 2008
    Just a note. VB.NET does initialization differently.

  • Anonymous
    February 18, 2008
    I suspect there is a good reason behind every 'confusing' behavior... I think I know why the following program has to behave that way but I find it confusing nevertheless. class Program {        static void Main() {            B b = new B();            b.Display();            b.OtherDisplay();        }    }    class A {         public void Display() {            Console.WriteLine("from A");        }    }    class B : A {        new void Display() {            Console.WriteLine("from B");        }        public void OtherDisplay() {            Display();        }    }

  • Anonymous
    February 18, 2008
    I'll make a guess: If it's done the 'obvious' way, there exists a point (during the base constructor) where the derived readonly field exists but is 0. There exists a later point where the same (readonly) field has a different value. That's presumably not good. If it's done the other way around, then by the time any code runs which has access to the new object (the ObjectConstructor), all the readonly fields are fully initialized. I'll guess further that this behaviour is due to some kind of weirdness in C# whereby the object's dynamic type is the type of the full object throughout construction, rather than the dynamic type changing as more constructors get called, as happens in C++.

  • Anonymous
    February 18, 2008
    Correct, though your characterization of the CLR semantics for the dynamic type of an object as "weirdness" is itself somewhat weird. I would argue that it is more weird for the dynamic type of an object to change throughout that object's lifetime!  The type of an object is a constant from the moment it is created through to the moment it is destroyed. Mutable objects are hard enough to reason about; objects that mutate their own runtime type are truly odd.

  • Anonymous
    February 18, 2008
    A useful addition to C# would be to allow field initializers in true C++ fashion: public class Stack<T> {    private T[] items;    private int count;    public Stack<T>(int capacity) : items(new T[capacity]) { }    // ... } This corresponding IL code is currently tolerated by the CLR, and it solves several problems with assertions regarding nullability of fields.

  • Anonymous
    February 19, 2008
    The comment has been removed

  • Anonymous
    February 21, 2008
    Removing readonly modifier does not make any difference in the result. Am I missing something?

  • Anonymous
    February 28, 2008
    The comment has been removed

  • Anonymous
    March 11, 2008
    Welcome to the forty-first Community Convergence. The big news this week is that we have moved Future

  • Anonymous
    March 12, 2008
    Wouldn't the following order make the most sense (since base initializers have no way of referring to derived variables, but the reverse isn't true)?  Or can you provide a counter-example? base initializer derived initializer base constructor derived constructor

  • Anonymous
    March 25, 2008
    I think your point is: If the the initialization is not done first, then if the Derived class tried to reference the base.baseFoo.Bar() function in it's constructor baseFoo may be a null reference, correct? public Derived()        {            base.baseFoo.Bar();            Console.WriteLine("Derived constructor");        }

  • Anonymous
    March 27, 2008
    When we call Derived() constructor, The Base object should be created first, include all of its field. So of cause we can use the member of Base class in the Derived() constructor. But why should we initialize the Derived's readonly field before the Base field? I think it just let the compiler developing a little easy.

  • Anonymous
    March 30, 2008
    The comment has been removed

  • Anonymous
    March 31, 2008
    I agree with Arkain. It makes sense that "Since the initializer's job is to call the ancestor's constructor, there'd be nothing for the initializers to do."

  • Anonymous
    April 01, 2008
    readonly modifiers causes a kind of static initialization. that's why it will be initialized in reverse manner.

  • Anonymous
    April 04, 2008
    The comment has been removed

  • Anonymous
    May 17, 2008
    C# Code behind is fabulous. Thanks. JFront

  • Anonymous
    May 21, 2008
    The comment has been removed

  • Anonymous
    June 02, 2008
    For me, inheritance is often a headache. In particular, in what order is everything initialized? ...

  • Anonymous
    June 03, 2008
    The comment has been removed

  • Anonymous
    June 04, 2008
    Base initializer or Base constructor prior to ObjectConstructor might be helpful to do better for objectconstructor.

  • Anonymous
    July 12, 2008
    我们在实现类的继承时,创建派生类的实例时,基类与派生类的实例字段都要进行实例化,他们的构造函数都需要调用,那执行的顺序是怎样的呢?一起来做做这个测试题吧。

  • Anonymous
    August 04, 2008
    If someone has mentioned this above and I missed it, I apologize for repeating, but one potentially "snagly" situation I can think of is this: constructor of Base calls a method of Base which is overridden in Derived and whose overridden behavior references a (non-static initonly) member of Derived which has not yet been initialized.

  • Anonymous
    August 31, 2008
    Hi All, What i feel is that it always that initializers run first; and the pointer to base is also a member of derived class and in order to initialize that member the control passes to base class. All i mean to say is that the process of object creation and initialization always starts first at the derived class only. Where as in order to construct or initialize the derived object completely we need instance of base class as well; that is why the partially initialized derived object is pushed to stack for time being and control moves to base class. As soon as the base class is completely initialized the partially initialized object is popped from the stack and initialized completely using the base object which is now available. So in the meantime when our partially initialized object of derived class is taking some rest in the stack, the initialization of base object completes and we feel that the process has started from the base class only. Whereas this is just an illusion. Have a look at the sample code below and then just read the sequence of activities: class classBase {    int a = 0;    int b = 1;    public void classBase()    {       Console.WriteLine("Base Class Constructor called");    } } class classDerived : classBase {   int c = 2;   int d = 3;    public void classDerived()    {       Console.WriteLine("Derived Class Constructor called");    } } 1.) Initialization of derived class object starts 2.) All members except the reference to base class are initialized 3.) When .NET tries to initialize the reference to object base class; it is found to be NULL 4.) The partially initialized object of derived class is pushed to STACK 5.) Initialization of Base class starts 6.) All members are initialized since there is no dependency 7.) CONSTRUCTOR of base class is called to complete process of object creation of Base class   8.) Code in CONSTRUCTOR of base class executes. 9.) Now control returns to derived class object 10.) Partially initialized object of derived class is POPPED from the Stack. 11.) Initialization of Derived class object completes as now we have reference to base class object as well. 12.) CONSTRUCTOR of derived class is called to complete process of object creation of Derived class   13.) Code in CONSTRUCTOR of Derived class executes. namaste!

  • Anonymous
    August 31, 2008
    I just noticed that 'PK' and me are trying to convey almost one and the same thing. Before this i was doubtfull about my post but now i am 100% sure that mine (and PK's as well) is the right answer for the question asked in this post :) namaste!

  • Anonymous
    October 01, 2008
    This is why i always initialize variables in the constructor. That way u dont have to worry about it.

  • Anonymous
    January 18, 2009
    Consider this program which attempts to mutate a readonly mutable struct. What happens? struct Mutable

  • Anonymous
    May 08, 2009
    Exactly as Bill Sanders said. This init order is to support a polymorhic method call in the constructor.

  • Anonymous
    June 27, 2009
    it always that initializers run first- this is a key point

  • Anonymous
    November 09, 2010
    It is actually very sound to run initializes first from derived to base while constructors from base to derived. The basic purpose of the constructor is to assign some values to the member fields. Right? Now to assign the member fields the runtime must first be able to see what are those member fields? Now just have a look at the initalizer. What is an initializer? Very simply it is a statement assigning a value upon declaration. Now when an object is create for a particular class the fields of the class should be first declared correctly. Then it’s base class and then base of that base class until we reach system. Object. This is like constructing a building where engineers create the outer most walls of the structure first which in this case will be the fields of the outer most class. And then the support pillars inside the building. Now think of constructors as creating floors in the building. You create first floor that is the system. Object’s constructor, then you create the first floor (next class after system. object) and then you create the next till you get to last class which is the class whose object is being initialized.

  • Anonymous
    November 09, 2010
    It is actually very sound to run initializes first from derived to base while constructors from base to derived. The basic purpose of the constructor is to assign some values to the member fields. Right? Now to assign the member fields the runtime must first be able to see what are those member fields? Now just have a look at the initalizer. What is an initializer? Very simply it is a statement assigning a value upon declaration. Now when an object is create for a particular class the fields of the class should be first declared correctly. Then it’s base class and then base of that base class until we reach system. Object. This is like constructing a building where engineers create the outer most walls of the structure first which in this case will be the fields of the outer most class. And then the support pillars inside the building. Now think of constructors as creating floors in the building. You create first floor that is the system. Object’s constructor, then you create the first floor (next class after system. object) and then you create the next till you get to last class which is the class whose object is being initialized.