Scott Wiltamuth's Visual Studio blog

Anders on virtual calls from constructors in C# and .NET

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 

Published Friday, January 14, 2005 11:59 AM by scottwil

Comments

 

Fabulous Adventures In Coding said:

January 14, 2005 3:18 PM
 

Stuart Ballard said:

Is it possible to hack around the limitation that you can't run code before the base class constructor runs?

I'm imagining code like this:

public Foo() : this(DoSomething()) {}
private Foo(object o) : base() {}
private object DoSomething() {
this.field = someValue;
}

Seems like DoSomething() ought to get called here before the base constructor. But perhaps the compiler won't let this through...
January 14, 2005 1:10 PM
 

Scott Wiltamuth said:

No, you can't do that.

According to 10.10.1 in the language reference: "An instance constructor initializer cannot access the instance being created. Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple-name."

--Scott
January 14, 2005 4:31 PM
 

Chris Nahr said:

While the code is technically correct, Eric may have found a logic bug after all.

If you instantiate TrinityProject with a parameter of true, the ShowProjectUI override is called during construction -- but at this point, the instance field dismissAsserts is still false because it has not yet been set!

To me, this code looks like the TrinityProject ctor should repeat the call ShowProjectUI after dismissAsserts has been set. If this is not the intention, a clarifying comment might be in order ("set field for ShowProjectUI calls after construction").
January 15, 2005 12:22 AM
 

Frederik Slijkerman said:

In the Delphi (Object Pascal) language that Anders worked on originally, you have the same behavior as in C#... except that you have to call the base constructor explicitly.

constructor TTrinityProject.Create(Dismiss: Boolean);
begin
FDismiss := Dismiss;
inherited Create;
end;

procedure TTrinityProject.ShowProjectUI;
begin
// FDismiss is already set, even when called from the inherited constructor.
...
end;

I think this is the best solution. You can call a derived virtual method from a constructor which is often useful, *and* you have full control over when the inherited constructor runs. The same applies to destructors BTW.
January 15, 2005 4:28 AM
 

Chris Nahr said:

I agree that the Delphi way looks like the best solution. I wonder why Microsoft dropped this feature in C#?
January 15, 2005 8:09 AM
 

Kyle Lahnakoski said:

If this is a problem to be solved, then it is a problem with dynamic dispatch and how it is interpreted inside and outside a module.

It seems to me there are a whole set of dynamic dispatch 'gotchas' having to do with module scope. I had the unpleasant luck to find one of my own in Java. (http://arcavia.com/kyle/Analysis/DynamicDispatchProblem.html)
January 15, 2005 5:56 PM
 

DrPizza said:

"Unlike C++, C# (or, more correctly, .NET) immediately assigns a newly allocated object its true type. "

I don't agree that this is true.

Oh, the block of memory might be big enough to be the newly allocated type. And if you ask the system, "what's the type of this block of memory", it will tell you the newly allocated type.

But until all the constructors have run, it's *not* an object of that type. It's something *about* to be of that type.
January 17, 2005 3:14 AM
 

Chris Nahr said:

What do you mean, you "don't agree"? You know something about .NET that we don't?

.NET isn't C++. The system provides well-defined initial default values for all data fields as soon as managed memory has been allocated. If inline initializers are present, their values are used instead. The ctors then may change any data fields as desired, but that's completely optional.

Therefore, yes, you do have an object of that type. Except that some data fields don't have their final post-construction values yet.
January 17, 2005 5:03 AM
 

James Hebben said:

You can half get around this behaviour by taking advantage of the fact that specific field initialisers are called before the constructor (or base constructor) is called. This is a documented aspect of the C# language.

As a general rule I avoid calling virtual functions from a constructor, although I recognise that in this case the developer has less control over the base class. As this discussion shows, messing with the boundaries of the language throws up more questions than answers.
January 17, 2005 7:02 AM
 

Stuart Ballard said:

James: Does that mean you can do something like:

object dummy = Initialize();
private object Initialize() {
// do whatever here
}

and get around the limitations?
January 17, 2005 10:58 AM
 

Dan Shappir said:

I agree with DrPizza regarding the object not being logically of a type before its ctor has run. To me an object can be considered to be of a certain type only when its values are consistent with its invariants. Andres' statement about NET always zeroing out the memory of a newly allocated object before executing any ctor, is thus correct but invalid. Likewise Chris' statement about well-defined initial default values for all data fields. While a zero value is well defined, it may not be valid.

I don't find the C++ approach to be odd, but that may simply be the result of using that language for over 10 years. I am wondering why Andres would call this approach incorrect.
January 18, 2005 1:51 AM
 

James Hebben said:

Stuart Ballard wants to know if one can set a field by calling a method.

The answer is that you can BUT the method MUST be static. Attempting to initialise a field by calling an instance method is a compile time error.
January 18, 2005 5:23 AM
 

aaron weiker said:

January 18, 2005 2:04 PM
 

greg::blog said:

January 20, 2005 8:00 PM
 

greg::blog said:

January 20, 2005 8:01 PM
 

piers7 said:

Strangely enough I had this exact problem at work this week, which set me wondering what the 'best practice' solution is.

After all there *are* instances where it might be desirable for subclasses to override some setting, but you need to reference the setting in the (base) constructor for part of the object's core initialization.

Deferring the virtual-referencing code till some kind of Init() method just raises more issues. Do you require the derived class to call Init() in its constructor (what if it forgets to), or the client (yuk). And if the former, what happens if the derived class is further subclassed, and *that* class wants to override the property too.

The obvious answer would be to be able to call base() explicitly at somepoint in your constructor, rather than have it always run first, but I'm sure that raises a whole shed load of other issues, which

Seems to me that the sequence issues in object construction is where polymorphism falls down a bit.
January 23, 2005 2:41 AM
 

Patric Johansson said:

Trackback
January 26, 2005 9:47 AM
 

clintoen said:

The Getting Started with C++ section gives advice about learning C++. Learn from our C++
tutorials, or test your programming knowledge with the C++ MegaQuiz. You can subscribe to Code Journal, a free biweekly programming newsletter.
January 26, 2005 9:43 PM
 

Don Syme said:

People interested in the theory and practice of initialization may be interested in my paper "An Alternative Approach to Initializing Mutually Referential Objects", available at http://research.microsoft.com/users/dsyme/publications.aspx. I'll put an entry on my blog about it. Hope you enjoy it! If you have interesting examples of initialization puzzles then please let me know.
January 29, 2005 3:21 AM
 

James Newton-King - Newtonblog said:

When growing up from time to time when I gave my name to someone I was often asked if I had any any relation...
July 1, 2006 3:21 AM
 

James Newton-King said:

When growing up from time to time when I gave my name to someone I was often asked if I had any any relation
August 18, 2006 4:49 AM
 

Fabulous Adventures In Coding said:

One of the truly great things about working here is that Distinguished Engineers take time out of their

October 10, 2007 10:03 AM
 

Marco Dorantes' WebLog said:

Favorites Build Providers for Windows Forms Curso del.icio.us-adev Dr. Dobbs Web 2.0 and the Engineering

February 17, 2008 10:21 PM
 

Scott Wiltamuth s Visual Studio blog Anders on virtual calls from | Wood TV Stand said:

June 2, 2009 6:14 PM
 

Scott Wiltamuth s Visual Studio blog Anders on virtual calls from | work from home said:

June 16, 2009 7:14 AM
 

Scott Wiltamuth s Visual Studio blog Anders on virtual calls from | patio umbrella said:

June 17, 2009 11:55 PM
New Comments to this post are disabled

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker