this.Blog.Find(entry => entry.IsHelpful);
 Saturday, July 19, 2008
Don't call virtual methods in a constructor

I ran into a piece of code today similar to this:

public class Base
{
    public Base()
    {
        this.Initialize();
    }

    protected virtual void Initialize()
    {
        Debug.WriteLine("Base.Initialize()");
    }
}

public class Derived : Base
{
    public Derived() : base() {}

    protected override void Initialize()
    {
        base.Initialize();
    }
}

public class FurtherDerived : Derived
{
    public FurtherDerived() : base() {}

    protected override void Initialize()
    {
        Debug.WriteLine("FurtherDerived.Initialize()");
    }
}

public class Tester
{
    public static void Main(string[] args)
    {
        Base myBase = new Base();

        Derived myDerived = new Derived();

        FurtherDerived myFurtherDerived = new FurtherDerived();

        Console.ReadLine();
    }
}

See any problems here?  Even though the compiler lets you call virtual methods from a base constructor, it's generally a "bad idea".  That's because the constructor in the derived classes defers its construction up to its base classes before it executes its own constructor (even if we don't explicitly call the base constructor like in the example).  Yet the call to a virtual function in the base class constructor is executed in the derived class implementation.  This can cause subtle, unexpected problems.

In the example above, things "happen" to go well.  It actually runs just fine.  It outputs the following as you'd probably expect:

Base.Initialize()
Base.Initialize()
FurtherDerived.Initialize()

But that's only because we didn't do anything significant in the derived constructors.  What happens if we change the FurtherDerived class:

public class FurtherDerived : Derived
{
    StringBuilder _sb;

    public FurtherDerived() : base()
    {
        _sb = new StringBuilder();
    }

    protected override void Initialize()
    {
       _sb.Append("FurtherDerived.Initialize()");
       Debug.WriteLine(_sb.ToString());
    }
}

Guess what happens when we run this code? FAIL!!! The dreaded, "Object reference not set to a reference of an object". Since the Initialize() method in the FurtherDerived class is called from the Base constructor, we are trying to access the _sb class level parameter before it's ever initialized.  We are calling a method on an class we know has not been fully constructed.  That ain't good.

The lesson is - never call a virtual method from a constructor.  At best, you are introducing a bug waiting to happen.  At worst, you've already done so.


Kick it on DotNetKicks.com
Friday, July 18, 2008 11:57:17 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  .Net | Back To Basics | C#