Подтвердить что ты не робот

С# Как выполнить код после построения объекта (postconstruction)

Как вы можете видеть в приведенном ниже коде, метод DoStuff() получает вызов перед Init() во время построения дочернего объекта.

Я в ситуации, когда у меня есть множество дочерних классов. Поэтому повторение вызова метода DoStuff() непосредственно после Init() в конструкторе каждого дочернего элемента не будет изящным решением.

Можно ли создать какой-либо конструктор post в родительском классе, который будет выполнен после дочернего конструктора? Таким образом, я мог бы вызвать метод DoStuff().

Если у вас есть другая идея дизайна, которая могла бы решить мою проблему, я тоже хотел бы это услышать!

abstract class Parent
{
    public Parent()
    {
        DoStuff();
    }

    protected abstract void DoStuff();
}

class Child : Parent
{
    public Child()
    // DoStuff is called here before Init
    // because of the preconstruction
    {
        Init();
    }

    private void Init()
    {
        // needs to be called before doing stuff
    }

    protected override void DoStuff() 
    {
        // stuff
    }
}
4b9b3361

Ответ 1

Как насчет этого:

abstract class Parent
{
    public Parent()
    {
        Init();
        DoStuff();
    }

    protected abstract void DoStuff();
    protected abstract void Init();
}

class Child : Parent
{
    public Child()
    {
    }

    protected override void Init()
    {
        // needs to be called before doing stuff
    }

    protected override void DoStuff() 
    {
        // stuff
    }
}

Ответ 2

Если у вас сложная логика для построения ваших объектов, рассмотрите FactoryMethod.

В вашем случае я бы использовал его как простой

public static Parent Construct(someParam)

который принимает некоторый параметр и на его основе решает, какой дочерний класс должен создать экземпляр. Вы можете удалить вызов метода DoStuff() из конструктора и вызвать его внутри Construct() в новом экземпляре.

Кроме того, вам следует избегать вызовов виртуальных/абстрактных методов в конструкторах. См. Этот вопрос для получения дополнительной информации: Виртуальный вызов участника в конструкторе

Ответ 3

Я бы настоятельно рекомендовал использовать Factory как шаблон.

Если это возможно:

1. Вставьте все ваши дочерние элементы и абстрактный класс в отдельную сборку.

2. Объявляйте ctors дочерних элементов, как внутренние методы, поэтому никто из этой сборки не может построить их, вызвав ctor.

3. Внедрите класс Factory для построения типа объектов с указанными объектами, который obviuoly будет форсировать вызов абстрактного метода DoStuff() после фактического создания anobject, но перед возвратом его вызывающему.

Хорошая вещь: это даст вам также дополнительный уровень абстракции, поэтому, если в будущем вам понадобится еще несколько функций вызова или любой другой тип логической сложности, что вы будете нужно просто добавить их в ваш класс Factory.

То есть.

Привет

Ответ 4

Как уже упоминалось, вы должны использовать шаблон Factory.

public class Parent
{
    public Parent()
    {            
    }

    public virtual void PostConstructor()
    {
    }
}

public class Child : Parent
{
    public override void PostConstructor()
    {
        base.PostConstructor();

        // Your code here
    }
}

public void FactoryMethod<T>() where T : Parent
{
    T newobject = new T();
    newobject.PostConstructor();
}

Ответ 5

Исправление: В соответствии с этим ответом вы не можете определить, когда конструктор базового класса вызывается во время построения подкласса.

например. Это не работает:

public Child()
// DoStuff is called here after Init
// because of the overridden default constructor
{
    Init();
    base();
} 

Итак, да, как отмечали другие, если последовательность событий имеет значение, то базовый класс должен уметь это учитывать, объявляя абстрактные методы в порядке или (еще лучше), если реализация дочернего класса DoStuff представляет последовательность событий:

protected override void DoStuff()
{
    Init();
    base.DoStuff();
}

Ответ 6

DoStuff является абстрактным. Просто вызовите Init из верхней части DoStuff.

Ответ 7

Вместо использования метода abstract, который потребует от вас реализации метода во всех классах потомков, вы можете попробовать:

public class Parent
{
    public Parent()
    {
        PostConstructor();
    }


    protected virtual void PostConstructor()
    {
    }
}

public class Child : Parent
{
    protected override void PostConstructor()
    {
        base.PostConstructor();

        /// do whatever initialization here that you require
    }
}

public class ChildWithoutOverride
{
    /// not necessary to override PostConstructor 
}

Ответ 8

В приложениях WPF вы можете отложить вызов DoStuff() с помощью Dispatcher:

abstract class Parent
{
    public Parent()
    {
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(this.DoStuff));
    }

    private void DoStuff()
    {
        // stuff, could also be abstract or virtual
    }
}

Однако не гарантируется, что DoStuff() вызывается сразу после конструктора.

Ответ 9

Позвольте мне представить общее решение, используя некоторые функции С#. Обратите внимание, что это решение не требует использования шаблона factory или вызова чего-либо после создания объекта, и оно работает с любым классом, только реализуя интерфейс с помощью одного метода. Сначала мы объявляем интерфейс, который должны реализовать наши классы:

public interface IInitialize {
    void OnInitialize();
}

Затем добавим статический класс расширения для этого интерфейса и добавим метод Initialize:

public static class InitializeExtensions
{
    public static void Initialize<T>(this T obj) where T: IInitialize
    {
        if (obj.GetType() == typeof(T))    
            obj.OnInitialize();
    }
}

Теперь, если нам нужен класс и все его потомки, чтобы вызвать инициализатор сразу после того, как объект полностью сконструирован, все, что нам нужно сделать, это реализовать IInitialize и добавить строку в конструкторе:

public class Parent : IInitialize
{
    public virtual void OnInitialize()
    {
        Console.WriteLine("Parent");
    }

    public Parent()
    {
        this.Initialize();
    }
}

public class Child : Parent
{
    public Child()
    {
        this.Initialize();
    }

    public override void OnInitialize()
    {
        Console.WriteLine("Child");
    }
}

public class GrandChild : Child
{
    public GrandChild()
    {
        this.Initialize();
    }

    public override void OnInitialize()
    {
        Console.WriteLine("GrandChild");
    }
}

Фокус в том, что когда производный класс вызывает метод расширения Initialize, это будет подавлять любые вызовы, не сделанные из фактического класса.