When Fields are Initialized, or "Lies Reflector Told Me"
The other day a coworker came to me with a Tricky Language Question. He and another chap had just finished working through a bug that had arisen due to a misunderstanding of C# constructor and field initialization order. The question?
In a derived class, when does field initialization occur, relative the derived and base constructor code?
Specifically, what does this output?
class Print
{
public Print(string message)
{
Console.Out.WriteLine(message);
}
}
class Base
{
public Print baseField = new Print("Base Field");
public Base()
{
new Print("Base Constructor");
}
}
class Derived: Base
{
public Print derivedField = new Print("Derived Field");
public Derived()
{
new Print("Derived Constructor");
}
}
class Program
{
static void Main()
{
new Derived();
}
}
I don't often give much thought to the "field vs. base class constructor" thing, but I knew that the Base constructor would be called before the Derived constructor, and I'd seen disassembled code in Reflector that showed field initialization as if it were the first code executed in a constructor. My guess was:
- Base Field
- Base Constructor
- Derived Field
- Derived Constructor
"Not so," said my coworker. The actual order is
- Derived Field
- Base Field
- Base Constructor
- Derived Constructor
The reason for this is given in C# Language Specification section 17.10.3, Constructor execution:
Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.
"What's the problem here?" you may be wondering - the Base code doesn't know anything about the Derived fields, so why go out of our way to make sure the field initializers are called before the Derived constructor?
Vitual methods
Virtual methods are the problem. If a virtual method is defined in Base and overridden in Derived, the overridden method may reference the new fields added to Derived. If the virtual method is called from the Base constructor, then we need those fields to be initialized before the constructor is called. Initializing fields even before calling base class constructors ensures that this is so.
Or does it? What if the field I'm accessing in a overridden method in the derived class doesn't have a field initializer, that method is called from the base constructor, and the field value is set in the derived constructor? In this case, the field won't be initialized before the method is called - it will still have the default value for its type.
So how to do we safely call virtual methods in constructors? We don't. You can't guarantee what code is going to go into a derived class's virtual method, so you never know what's going to happen.
Back to Reflector
Remember a few paragraphs ago when I said that Reflector told me that field initialization acted like it was an assignment statement at the beginning of a constructor? Well, I did, and I wanted to see whether I was misremembering, so I compiled my sample code and threw the assembly into Reflector. Here's what I saw:
I felt somewhat vindicated - this matched my memory. For a lark, I took this code (and the matching code Reflector showed me for the Base class), compiled it, ran it, and got:
- Base Field
- Base Constructor
- Derived Field
- Derived Constructor
The more I thought about this, though, the worse I felt. How could Reflector let me down like this? Isn't it just looking at the IL and translating into C#? I poked around a little more, and instead of just double-clicking on the Derived constructor, I right-clicked on the Derived class node in the navigation tree and picked Disassemble. Lo and behold:
So, Reflector does know what's going on—you just have to ask nice. To recap,
- if you know which Reflector action to choose,
- you remember about field initializers running before even base class constructors, and
- you keep careful track of virtual methods called from constructors
Reflector can tell you what's going on in your code. Forget any of those things, and you're lost.