Years ago, I had the good fortune to work with Eric Lippert on VBScript and JScript.  I was a young program manager and he was an even younger developer who initially set himself apart by always wearing a white hat.  Maybe the subliminal message was that he was a good guy?  Well, subliminal message or not, that is definitely the case.  Eric sent me email a few weeks ago with an interesting design question.  Despite being involved in all of the design discussions on the issue, I couldn't remember the answer.  Anders could, and I provide the q & a below.

 

<question>

I just had an interesting conversation that exposed what looks to me like a design flaw in C#.  I thought I might run it by you, if you don't mind, to see if you can explain to me that there really is a good reason for this.

 

Here's the deal.  Consider a C++ derived class which is destructed.  The derived class destructor runs, then the base class destructor runs.  When a base class destructor calls a virtual method, the compiler ensures that the base class's version of the virtual method is called, rather than the derived class's version. 

 

This seems initially odd, but makes sense -- the derived class's implementation almost certainly relies upon state which has just been destructed, so you shouldn't call it after the derived destructor runs.

 

Now consider page 346 of "The C# Programming Language", where you point out that a base class constructor can call a virtual method on a derived class before the state of the derived class is fully initialized.

 

It came up because one of our testers has the following code:

public class Project // base class owned by another team, we can't change it easily
{
  public
Project()
  {
    this
.ShowProjectUI();
  }

  public virtual void ShowProjectUI() { }
}

public class TrinityProject : Project // our code
{
  private bool dismissAsserts;

  public TrinityProject(bool dismiss): base()
  {
   
this.dismissAsserts = dismiss;
 
}

  public override void ShowProjectUI()
  {
   
if (this.dismissAsserts)
      CreateDismisserThread();
  }
}

Since the base class constructor, and hence the virtual method, runs before the derived class constructor, the dismiss asserts member is only ever set to its default value when the initial UI is created.  The caller's parameter is essentially ignored.

 

I would expect C# to either (a) call the base class method if the derived class is not fully constructed (which seems bad), or (b) provide a syntax for initializing derived class members using constructor arguments before base class constructors run (which seems good, and is what C++ does).

 

My two questions to you are then:

  • what's up with this?  Is there some goodness here that I'm missing out on?  Because this seems like a big old gotcha waiting to happen, calling virtual methods before the object is constructed.
  • any thoughts on how my tester buddy Ian can make his derived class work without petitioning the owner of the base class to stop calling virtual methods from the constructor?

Thanks!

 

Eric

</question>

 

<answer>

We (C#, .NET guidelines, FxCop) discourage calling virtual methods from constructors. Forbidding it outright would be hard to do--even if we only allowed constructors to call non-virtual methods, those methods could in turn call virtuals. Also, it is occasionally useful.

 

C++ takes an object through a progression of type identities as a constructor executes--basically, each constructor updates the v-table after its call to the base constructor returns. As a result, virtual calls in a constructor never go deeper than the constructor's level of inheritance. This scheme provides a certain amount of protection against observing uninitialized instance variables in virtual methods, but I don't think it is a particularly useful or intuitive model. Indeed, the notion that an object goes through a progression of base types until it finally reaches its true type (and vice versa on destruction) is rather odd, and you could even argue incorrect.

 

Unlike C++, C# (or, more correctly, .NET) immediately assigns a newly allocated object its true type. Thus, virtual call dispatch is consistent across the entire lifetime of the object, but it is possible to receive virtual calls before execution reaches a constructor body. The big difference between C++ and .NET is that .NET always zeroes out the memory of a newly allocated object before executing any constructor code. So, while virtual methods may observe the zeroed out state of instance variables, they will never observe an undefined state and type safety is not compromised.

 

BTW, .NET's behavior is consistent with most other OOP systems, Java included. In fact, I don't know of any language but C++ that uses the "multiple personalities" approach to object construction.

 

Anders

</answer>

 

Though I'm several management layers away from the daily work of language design, I still enjoy thinking about issues like this one.

 

--Scott