Unknown's avatar

About ericlippert

http://ericlippert.com

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

As you might have figured out, the answer to last week’s puzzle is “if the constructors and initializers run in their actual order then an initialized readonly field of reference type is guaranteed to be non null in any possible call. That guarantee cannot be met if the initializers run in the expected order.”

Suppose counterfactually that initializers ran in the expected order, that is, derived class initializers run after the base class constructor body. Consider the following pathological cases:

class Base
{
  public static ThreadsafeCollection t = new ThreadsafeCollection();
  public Base()
  {
    Console.WriteLine("Base constructor");
    if (this is Derived) 
      (this as Derived).DoIt();
    // would deref null if we are constructing an instance of Derived
    Blah();
    // would deref null if we are constructing an instance of MoreDerived
    t.Add(this);
    // would deref null if another thread calls Base.t.GetLatest().Blah();
    // before derived constructor runs
  }
  public virtual void Blah() { }
}
class Derived : Base
{
  readonly Foo derivedFoo = new Foo("Derived initializer");
  public DoIt()
  {
    derivedFoo.Bar();
  }
}
class MoreDerived : Derived
{
  public override void Blah() 
  { 
    DoIt(); 
  }
}

Calling methods on derived types from constructors is a bad idea but it is not illegal. And stuffing not-quite-constructed objects into global state is risky, but not illegal. I’m not recommending that you do any of these things — please, do not, for the good of us all. I’m saying that it would be really nice if we could give you an ironclad guarantee that an initialized readonly field is always observed in its initialized state, and we cannot make that guarantee unless we run all the initializers first, and then all of the constructor bodies.

Note that of course if you initialize your readonly fields in the constructor then all bets are off. We make no guarantees as to the fields not being accessed before the constructor bodies run.

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 they 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!

Immutability in C# Part Eight: Even More On Binary Trees

Last year we declared a relatively simple interface to represent an immutable binary tree. We noticed that it was different from every other interface that we’ve declared so far, in that it really said nothing at all about the immutability of the tree. One normally thinks of immutable data types not in terms of their shape, but rather in terms of what operations may be performed on the data type. Operations are usually either to query the object somehow, or to “modify” it by producing new modified versions of the old immutable object.

Continue reading

Immutability in C# Part Five: LOLZ!

My sadly soon-to-be-erstwhile coworker Cyrus made me a lolgeek shirt to go with this series of blog articles:

ShirtFront
ShirtBack

Cyrus, needless to say, is a big goof. Thanks, dude!


Commentary from 2022: When I wrote this post 14 years ago Cyrus was about to leave Microsoft and go work for… Google? maybe? I don’t recall. Anyway, after some time we were very fortunate to get him back at Microsoft and last I checked he was once again on the C# compiler team.