Quiz: Virtual Methods


Under what circumstance will the following code print: “value != 42, what is wrong?”


 


I tested this quiz out on Lutz, his first question was “does it use reflection”… typical from the reflector guy 😉


Anyway, nope, no “funny business”… It is 100% verifiable code that would pass just about any code review.  


 


 


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 (16)

  1. Off the top of my head:

    What happens if a class DerivedAgain is declared that derives from Derived and replaces the value=42 in the constructor with something other than 42?

    Similarly, does C# guarantee that constructors are called in hierarchical order? If not, then base could declare a value, and change the 42 to something else.

    Another possibility has to do with the use of the name "value" – isn’t "value" a reserved word? Or is that only in the context of property accessors?

  2. Ken says:

    Since C# allows virtual dispatch on an object that hasn’t finished all its constructors, value won’t be set to 42 (yet) if Base’s constructor calls Method1, but it will execute Derived’s override, causing the "what is wrong?" message to be shown.

  3. my first thought was:

    public void MakeItWrong()

    {

    Base BaseObject = new Base();

    Derived DerivedObject = (Derived)BaseObject;

    DerivedObject.Method1();

    }

    but that feels utterly wrong 🙂

    On thinking properly, I’ll agree with Larry.

  4. JFo says:

    When the base class looks like so:

    public class Base {

    public Base() {

    Method1();

    }

    public virtual void Method1() {

    }

    }

    value will be 0 in the overrided method as the Derived constructor hasnt executed yet.

    Is it cheating if you know the FXCop rule? =)

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

  5. Wesner Moise says:

    The answer is as follows:

    In C#, a constructor call works as follows:

    public Derived()

    {

    // derived class initializers are initialized in the order they are declared

    // base constructors are called (and base initializers are initialized)

    // body of derived constructor is called

    }

    So, in the following example,

    public Derived : base

    {

    private value = 21;

    public Derived()

    {

    value = 42;

    }

    public override Method1()

    {

    }

    }

    public Base

    {

    public Base()

    {

    Method1();

    }

    public virtual Method1()

    {

    }

    }

    In this case, when Method1 is called by the Base constructor, the value of value is 21, not 42! Note, the value is not 0, because the initializers execute before the base constructors are ever called.

  6. Matthew W. Jackson says:

    Yep. If the base class calls a virtual method then weird things like this can happen.

    On a side note, don’t a LOT of Windows Forms classes set virtual properties in their constructors?

    There are a few ways to get around the problem if you absolutely NEED to call a virtual method during construction, but it seems like it would be nice if the framework could somehow optionally call a method on a base class after all constructors have executed.

    Maybe via an interface such as IInitializable (I don’t think that’s a word) would work, but there could be problems if multiple levels in the inheritance chain needed to be notified.

    Perhaps the runtime could let you hook up an event in your constructor that would fire exactly once for any object and then automatically unhook all listening events…It would probably work best as a protected event on the Object class, and could be used like this:

    class Base {

    protected Base() {

    this.ConstructionComplete += this.Initialize();

    }

    private void Initialize() {

    this.SomeVirtualMethod();

    }

    protected virtual void SomeVirtualMethod() {

    }

    }

    I think other people have suggested this kind of functionality before. Is this feasable at all? How deep into the runtime would a feature like this actually dig? Would there be any performance problems?

  7. Haacked says:

    This is probably not the answer you were looking for, but how about if you call it via C++?

    Derived* derived = new Derived();

    derived->Base::Method1();

    Assuming Base.Method1() looks like

    public virtual Method1()

    {

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

    }

    😉 yes. cheesy.

  8. sbjorg says:

    That’s one of my standard interview questions for SDEs. if a programmer doesn’t know the semantics of the programming language they use, he/she shouldn’t be programming. Sadly, I’ve come across way too many individuals who didn’t know. I’m glad to say they didn’t get hired.

    Of course, the real culprit is the constructor construct. BTW, in C++ the semantics are different. Hopefully some day we’ll realize our mistake and remove constructors from future languages.

  9. Sbjorg. That’s pretty silly IMO. Languages are incredibly complex beasts and it’s quite possible for someone to not know every little intricacy in it. A true developer would be someone who would have no problem picking up and understanding those intracacies when it came to it.

  10. Okay so it would fail just about any code review and definately counts as “funny business”. It’s here just for completeness… 😉

    Derived derived = (Derived)FormatterServices.GetUninitializedObject(typeof(Derived));

    derived.Method1();

  11. Stuart says:

    I figured out the answer to the quiz as written, but I ran a quick test and I can’t figure out why you *don’t* see this behavior if you remove the Derived constructor and just put "private int value = 42". I thought that field initializer expressions just got inserted into the constructor by the compiler?

  12. Stuart,

    The reason you don’t see this behavior if you put it into the field initializer is because of how the constructor is generated. The order in C# is:

    Field initializer for derived

    call base ctor

    call derived ctor

    Interestingly this is NOT the order in VB, where all of the base stuff (initializers + ctor bodies) run before all of the derived class stuff. Compare the output of the folloing two "identical" programs in VB and C#. The C# program outputs:

    baz running

    bar running

    foo running

    A ctor

    B ctor

    C ctor

    The VB code outputs

    foo running

    A ctor

    bar running

    B ctor

    baz running

    C ctor

    I’ll leave it as an exercise to the student to consider the cross-language inheritance implications.

    I enjoy your quizzes, Brad – keep them up!

    C# code

    —————————————————————-

    using System;

    class Util

    {

    public static int foo()

    {

    Console.WriteLine("foo running");

    return 1;

    }

    public static int bar()

    {

    Console.WriteLine("bar running");

    return 2;

    }

    public static int baz()

    {

    Console.WriteLine("baz running");

    return 3;

    }

    }

    class A

    {

    int a = Util.foo();

    public A() { Console.WriteLine("A ctor"); }

    }

    class B : A

    {

    int b = Util.bar();

    public B() { Console.WriteLine("B ctor"); }

    }

    class C : B

    {

    int c = Util.baz();

    public C() { Console.WriteLine("C ctor"); }

    }

    class App

    {

    static void Main()

    {

    new C();

    }

    }

    The "same" code in VB

    ———————————————————-

    Imports System

    Class Util

    Shared function foo as Integer

    Console.WriteLine("foo running")

    return 1

    End Function

    Shared function bar as Integer

    Console.WriteLine("bar running")

    return 2

    end Function

    Shared function baz as Integer

    Console.WriteLine("baz running")

    return 3

    end Function

    End Class

    Class A

    Dim A as Integer = Util.foo()

    public sub New()

    Console.WriteLine("A ctor")

    end Sub

    end class

    class B : Inherits A

    Dim B as Integer = Util.bar()

    public sub New()

    Console.WriteLine("B ctor")

    end Sub

    end class

    class C : Inherits B

    Dim A as Integer = Util.baz()

    public sub New()

    Console.WriteLine("C ctor")

    end Sub

    end class

    class App

    Shared Sub Main()

    Dim c As C = new C

    End Sub

    End Class

  13. AndrewSeven says:

    IIRC FxCop warns you about using a virtual call in the ctor