Pages

Thursday 17 September 2009

Why you shouldn't call Abstract Methods from your Constructor

Calling abstract methods from your constructor is bad practice as it denies your concrete class any opportunity to set itself up. This is particularly noticeable in IoC situations (e.g. Unit Testing). Here's an example...

abstract class BaseClass
{
    public BaseClass()
    {
        Setup();
    }

    public abstract void Setup();
}

class ConcreteClass : BaseClass
{
    public ConcreteClass()
    : base()
    {
    }

    public override void Setup()
    {
        // TODO: add setup code here...
    }
}

Well, that will work fine. But what if ConcreteClass is tightly coupled to another class. For instance, it could access the Configuration class...

class ConcreteClass : BaseClass
{
    public ConcreteClass()
    : base()
    {
    }

    public override void Setup()
    {
        string setting = ConfigurationManager.AppSettings["MySetting"];
    }
}

Again, no problems here. However; ideally, we would like to remove this tight coupling by implementing an IoC pattern...

class ConcreteClass : BaseClass
{
    private IConfigurationManager _configurationManager;

    public ConcreteClass()
    : this(new DefaultConfigurationManager())
    {
    }

    public ConcreteClass(IConfigurationManager configurationManager)
    : base()
    {
        _configurationManager = configurationManager;
    }

    public override void Setup()
    {
        string setting = _configurationManager.AppSettings["MySettings"];
    }
}

And this is where the problems arise. The Setup() method will now throw a NullReferenceException because the ConcreteClass's constructor has not been allow to run before its own instance methods have been called!

Unfortunately, there is nothing in the C# compiler or Code Analysis tools (to my knowledge) that will flag this as even a warning.

The solution is to recognise why you need to call the Setup method and before what other event. This normally gives you the opportunity to call it from an OnMyEvent() method.

No comments:

Post a Comment