C # Как выполнить код после постройки объекта (постконструкция) - PullRequest
38 голосов
/ 22 июля 2011

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

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

Можно ли каким-либо образом создать некий пост-конструктор вродительский класс, который будет выполнен после конструктора ребенка?Таким образом, я мог бы вызвать там метод 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
    }
}

Ответы [ 9 ]

18 голосов
/ 22 июля 2011

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

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

public static Parent Construct(someParam)

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

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

12 голосов
/ 22 июля 2011

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

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
    }
}
6 голосов
/ 10 сентября 2017

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

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, это подавляет любые вызовы, не сделанные из фактического класса.

4 голосов
/ 22 июля 2011

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

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

1 . Поместите всех своих детей и абстрактный класс в отдельную сборку .

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

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

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

То есть.

Привет

3 голосов
/ 21 апреля 2015

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

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();
}
1 голос
/ 09 сентября 2014

В приложениях 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() будет вызван сразу после конструктора.

1 голос
/ 22 июля 2011

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

1 голос
/ 22 июля 2011

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

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

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

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

protected override void DoStuff()
{
    Init();
    base.DoStuff();
}
0 голосов
/ 22 июля 2011

Вместо использования 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 
}
...