С# Как выполнить код после построения объекта (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
}
}
Ответы
Ответ 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
, это будет подавлять любые вызовы, не сделанные из фактического класса.