Создание свойств производного класса без вызова виртуального члена в конструкторе - PullRequest
0 голосов
/ 04 декабря 2018

У меня есть несколько вложенных классов, BaseOuter и BaseInner, и дочерние классы DerivedOuter и DerivedInner.BaseOuter имеет свойство BaseInner baseInner;, и когда я создаю экземпляр DerivedOuter, я хочу, чтобы тип времени выполнения свойства baseInner был DerivedInner.

Сначала я решил эту проблему, как показано ниже, используя виртуальныйинициализатор для создания экземпляра baseInner, который переопределяется в DerivedOuter.Это позволяет мне делать baseInner = new BaseInner(); против baseInner = new DerivedInner(); в соответствующих инициализаторах.

После того, как я заметил Resharper Warning и сделал немного больше чтения , я решил, что долженизменить это ... но как?

Несколько вещей приходят на ум.Я мог бы вызвать инициализатор после вызова конструктора, требуя вызова кода для выполнения var baseOuter = new BaseOuter(); baseOuter.Initialize();.Я, вероятно, могу использовать фабрику, но мне придется немного подумать об этом.И, наконец, возможно, есть недостаток дизайна во вложении классов, которое я хочу сделать?

Стоит отметить, что выполнение new BaseInner(); стоит дорого, и я не просто хочу его создать и выбросить.

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;
            }
        }
    }
}

Вывод

new BaseOuter
BaseOuter Constructor
    BaseOuter Initialize BaseInner
        BaseInner Constructor

new DerivedOuter
BaseOuter Constructor
    DerivedOuter Initialize DerivedInner
        BaseInner Constructor
        DerivedInner Constructor
DerivedOuter Constructor (finished)

Весь код можно найти здесь в этой .NET Fiddle .

1 Ответ

0 голосов
/ 04 декабря 2018

Вот подход.

Он делает то же самое.Разница в том, как производный класс "переопределяет" реализацию BaseInner.Это делается путем предоставления реализации конструктору.

internal class BaseOuter
{
    protected BaseInner baseInner;

    protected internal BaseOuter(BaseInner inner = null)
    {
        baseInner = inner ?? new BaseInner();
        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

    }

    protected internal class BaseInner
    {
        public int x;

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

internal class DerivedOuter : BaseOuter
{
    protected internal DerivedOuter()
     :base(new DerivedInner())
    {
        Console.WriteLine("DerivedOuter Constructor (finished)");
    }

    protected internal class DerivedInner : BaseInner
    {
        public double y;

        public DerivedInner()
        {
            Console.WriteLine("        DerivedInner Constructor");
            y = 2d;
        }
    }
}

Если вы хотите выполнить тотальное внедрение зависимостей, вы можете удалить значение по умолчанию из

protected internal BaseOuter(BaseInner inner = null)

Это означает, что BaseOuter будет полностью отделен от любой реализации BaseInner.

Несколько других предложений:

  • Я бы не вкладывал классы друг в друга, если бы в этом действительно не было необходимости.Он не используется часто, поэтому на первый взгляд он может кого-то запутать.
  • Если baseInner предполагается установить только при вызове конструктора, отметьте его readonly.(ReSharper, вероятно, предложит это.)
...