Quiz: Virtual Methods – Answer!


Well, glad to see many of you got it right, I think FxCop is doing its job!  Google helped me find a couple of posts on this already out there:
http://weblogs.asp.net/trichards/archive/2003/06/20/9026.aspx


http://dotnetjunkies.com/WebLog/mlevison/archive/2004/04/22/11976.aspx


 


Here is the full text I will add to the API Design Guidelines document.  Let me know what you think.


 


Consider the example below which prints out “value != 42, what is wrong” when a new instance of Derived is created.  The implementer of the derived class assumes that the value will be set to 42 before anyone can call Method1().  But that is not true because the base class’s constructor is called before the Derived class’s constructor finishes, so any calls it makes to Method1() causes the problem. 


 


public class Base


{


    public Base()


    {


        Method1();


    }


    public virtual void Method1() {


        Console.WriteLine(“In Base’s Method1()”);


    }


}


public class Derived: Base


{


    private int value;


    public Derived()


    {


        value = 42;


    }


 


    public override void Method1()


    {


        if (value == 42) Console.WriteLine(“value == 42, all is good”);


        else Console.WriteLine(“value != 42, what is wrong?”);


    }


}


 

Comments (12)

  1. There’s also this way of making it print out "value != 42, what is wrong?"

    public class MoreDerived : Derived

    {

    public override void Method1()

    {

    Console.WriteLine("value != 42, what is wrong?");

    }

    }

    It might help to show your Main() where you instantiate the types, or to make Derived sealed.

  2. Uwe says:

    IIRC, in C++, only Method1() of the BASE class is called in the BASE class constructor, because DERIVED is not yet constructed fully and therefore you cannot call Method1() of a "half-created" object.

    But maybe Bjarne just confused me completely… 🙂

  3. but only after derived object is created fully then only overeriding of virtual methods works .. a

  4. Brad

    I like the explanation – out of the answers given to the Quiz this is by far the clearest explanation of what is going on. This strikes me as a real gotcha – something which could take quite a while to track down…. I appreciate the insight.

    Ciaran

    p.s. Really like the SLAR – great to have this level of openness from MS.

  5. I think the good practice here is to make sealed the class which constructor calls a virtual method

  6. adam says:

    "But that is not true because the base class’s constructor is called before the Derived class’s constructor FINISHES, so any calls IT makes to Method1() causes the problem. "

    Should FINISHES not be "is called"? The finishes implies that it should in fact work, or at least it might work.

    i.e. the base class c’tor is called before the derived class c’tor all the way up the class hierarchy chain.

    Also the last "IT" should be specific, "the base class"

  7. RJ says:

    Interesting. With managed C++ declaring a normal class gives you the old c++ semantics (i.e. constructors do not use the vtable). But slapping __gc on that guy gives you the .net semantics. Well, it is nice that interoperability with the other .net languages is maintained.

    Regarding ‘finishes’ vs. ‘is called’: technically the base constructors are called on the first line of the derived constructor. But yes ‘finishes’ might still be an odd word to use. For me it brings up images of the constructors running asynchronously. 🙂

  8. Ken says:

    Uwe – part of the reasoning behind this is that in C++, the block of memory is not a valid object until the constructors all complete successfully – parts of the memory may not be initialized, internal tables and pointers may not be set up yet, etc. As far as Base’s constructor is concerned, the object is a valid Base object and nothing more at that point. In C# (and other .Net languages), the object springs into existance as a fully valid object with initialized memory and all the internals of the object already set up before the first user supplied constructor begins to execute. It may not be fully valid as the derived type, but as far as the runtime is concerned, the object is complete and usable, having the type of Derived even when it is in the Base constructor.

    Whether or not that is a desirable "feature" or not is open to debate. Either way you fail to meet some people’s expectations. In the case of C++, people wonder why their virtual functions aren’t called virtually from the constructor. In the case of C#, people wonder why their virtual functions are operating on non-fully-constructed objects. Both approaches have pitfalls.

    So why did C# choose the approach it did? The snarky response would be "that’s how Java works". The more practical response would be it makes things easier on the runtime if there aren’t partially valid objects floating around.

  9. btw this guideline is equally applicable to Java-on-JVM. Running the code below on JVM will get the same result.

    class Base

    {

    Base()

    {

    Method1();

    }

    void Method1()

    {

    System.out.println("In Base’s Method1()");

    }

    }

    class Derived extends Base

    {

    private int value;

    public Derived()

    {

    value = 42;

    }

    void Method1()

    {

    if( value == 42 )

    System.out.println("value == 42, all is good");

    else

    System.out.println("value != 24, what is wrong?");

    }

    }