Udostępnij za pośrednictwem


An initialization pattern to avoid problems calling virtual method in constructor

You are not supposed to call virtual methods in a constructor. The reason being that at the point you call a virtual method in the base class, the derived class has not been initialized. For example,

     class Base
    {
        public Base(int input)
        {
            VirtualMethod();
        }
        public virtual void VirtualMethod()
        {
        }
    }

    class Derived : Base
    {
        int _val;

        public Derived(int input) : base(input)
        {
            _val = 2 * input;
        }

        public override void VirtualMethod()
        {
            Console.WriteLine("Value is: " + _val.ToString());
        }
    }
 If you create an instance of Derived like 
     Derived d = new Derived(5);

the output is not 10 as expected because the constructor of Derived has not been called yet when VirtualMethod is called in Base's constructor. The question is what if you do need to call virtual methods during object creation time. Also do we have to keep track which method is virtual when we make method calls in a constructor? A non-virtual method today may very well be changed to virtual by someone else tomorrow. One solution I found is to have a separate Initialize method to call virtual methods. For example,

     class Base
    {
        public Base(int input)
        {
            // Called in Intialize instead of in Constructor
            // VirtualMethod();
        }

        public virtual void Initialize()
        {
            VirtualMethod();
        }

        public virtual void VirtualMethod()
        {
        }
    }

    class Derived : Base
    {
        int _val;

        public Derived(int input)
            : base(input)
        {
            _val = 2 * input;
        }

        public override void VirtualMethod()
        {
            Console.WriteLine("Value is: " + _val.ToString());
        }
    }

To create a Derived instance, you need to call

     Derived d = new Derived(5);
    d.Initialize();

The problem is that the caller has to remember to call Initialize after creating the object. I choose to hide the constructor and have a static factory method for clients to create objects, which guarantees that the Initialize method being called. For example,

     class Base
    {
        protected Base(int input)
        {
            // Called in Intialize instead of in Constructor
            // VirtualMethod();
        }

        protected virtual void Initialize()
        {
            VirtualMethod();
        }

        public virtual void VirtualMethod()
        {
        }
    }

    class Derived : Base
    {
        int _val;

        public static Derived CreateInstance(int input)
        {
            Derived d = new Derived(input);
            d.Initialize();
            return d;
        }

        protected Derived(int input)
            : base(input)
        {
            _val = 2 * input;
        }

        public override void VirtualMethod()
        {
            Console.WriteLine("Value is: " + _val.ToString());
        }
    }

Creating a Derived object will be like:

     Derived d = Derived.CreateInstance(5);

I can also go a step furthur to have a rule that the constructor can only be used to initialize fields and never to make method calls. All method calls go into Initialize method. This way there is no more need to keep track whether a method is virtual or not.

Comments

  • Anonymous
    October 05, 2008
    PingBack from http://www.easycoded.com/an-initialization-pattern-to-avoid-problems-calling-virtual-method-in-constructor/

  • Anonymous
    October 06, 2008
    .NET AnnouncingLullaby1.0RC1-anextensibleopensourceattribute-basedframeworkforcreatin...

  • Anonymous
    October 06, 2008
    .NET Announcing Lullaby 1.0 RC1 - an extensible open source attribute-based framework for creating RESTful

  • Anonymous
    February 27, 2014
    Does it really make sense to have it implemented in a way that your factory method is defined in child class? I think you're loosing all benefits of that virtual function here. I'd rather define factory method in base class.

  • Anonymous
    March 19, 2014
    That is really a bad practice, and if it is anything, it is an anti-pattern. One simple question, what if I want to get an instance from class Derived with no properties, just an empty one, for testing sake.... in this case you are forcing some value using your init function inside the constructor. I would do what Pawel said, or create a Facade to handle this initialization for me if it has more complex dependencies.