mardi 4 décembre 2018

Construct derived-class properties without virtual member call in constructor

I have some nested classes, BaseOuter and BaseInner, and child classes DerivedOuter and DerivedInner. BaseOuter has property BaseInner baseInner;, and when I instantiate DerivedOuter I want the runtime type of baseInner property to be DerivedInner.

I first solved this like I have below, using a virtual initializer to instantiate baseInner, that is overriden in DerivedOuter. That allows me to do baseInner = new BaseInner(); vs baseInner = new DerivedInner(); in the respective initializers.

After noticing a Resharper warning and doing a little more reading I decided I should change it... but how?

A couple of things come to mind. I could call the initializer after calling the constructor, requiring calling code to do var baseOuter = new BaseOuter(); baseOuter.Initialize();. I can probably use a factory, but I'd have to think about that a little. Lastly, perhaps there is a design flaw in the class-nesting I want to do?

It's worth pointing out that doing new BaseInner(); is expensive, and I don't simply want to create it and throw it away.

using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("new BaseOuter");
        var baseOuter = new BaseOuter();
        Console.WriteLine("\nnew DerivedOuter");
        var derivedOuter = new DerivedOuter();
    }

    class BaseOuter{
        protected BaseInner baseInner;
        public BaseOuter(){
            Console.WriteLine("BaseOuter Constructor");
            /*  lots of stuff I want in derived class */

            // This is an anti-pattern I want to avoid
            //https://www.jetbrains.com/help/resharper/2018.2/VirtualMemberCallInConstructor.html
            InitializeInner();
        }

        protected virtual void InitializeInner(){
            Console.WriteLine("    BaseOuter Initialize BaseInner");
            baseInner = new BaseInner();
        }

        protected class BaseInner{
            public int x;
            public BaseInner(){
                /* stuff that is needed in DerivedInner too */
                Console.WriteLine("        BaseInner Constructor");
                x = 2;
            }
        }       
    }

    class DerivedOuter : BaseOuter {
        public DerivedOuter() {
            Console.WriteLine("DerivedOuter Constructor (finished)");
        }

        protected override void InitializeInner(){
            Console.WriteLine("    DerivedOuter Initialize DerivedInner");
            baseInner = new DerivedInner();
        }

        protected class DerivedInner : BaseInner {
            public double y;
            public DerivedInner(){
            Console.WriteLine("        DerivedInner Constructor");
                y = 2d;
            }
        }
    }
}

All of the code can be found here in this .NET Fiddle.

Aucun commentaire:

Enregistrer un commentaire