Есть ли способ автоматического вызова определенного метода сразу после запуска всех конструкторов?
Я хочу, чтобы иметь возможность вызвать конкретный метод автоматически при построении производного объекта, однако я не могу думать, как это сделать. Следующий код иллюстрирует. Другой ответ рекомендовал OnLoad, но я делаю это для Unity на Mac, и OnLoad, похоже, не поддерживается моей платформой. Любые предложения?
public class Parent {
public Parent ()
{
// A. Stuff to do before child constructor code runs
DoThisAutomaticallyAfterConstruction();
}
public void DoThisAutomaticallyAfterConstruction()
{
// C. In this example, this will run after A, before B. I want it to run ABC
}
}
public class Child : Parent {
public Child () : base()
{
// B. Stuff to do here after parent constructor code runs
}
}
Ответы
Ответ 1
К сожалению, нет встроенного способа делать то, что вы хотите (это довольно часто запрашиваемая функция).
Одним из способов решения является реализация шаблона factory, в котором вы не создаете объекты, вызывая конструктор напрямую, а вместо этого реализуете статический метод для их создания. Например:
public class MyClass
{
public MyClass()
{
// Don't call virtual methods here!
}
public virtual void Initialize()
{
// Do stuff -- but may be overridden by derived classes!
}
}
затем добавьте:
public static MyClass Create()
{
var result = new MyClass();
// Safe to call a virtual method here
result.Initialize();
// Now you can do any other post-constructor stuff
return result;
}
и вместо выполнения
var test = new MyClass();
вы можете сделать
var test = MyClass.Create();
Ответ 2
Это звучит как хороший кандидат на factory. Сделайте все конструкторы частными или защищенными, требуя от потребителей вашего кода вызова метода factory, когда им нужен экземпляр вашего объекта. В методе factory вы используете оператор new
для создания объекта, а затем вызываете DoThisAutomaticallyAfterConstruction()
перед возвратом объекта.
ИЗМЕНИТЬ
A factory может быть статическим методом, или вы можете иметь объект factory. См., Например, Википедию об абстрактном шаблоне factory в http://en.wikipedia.org/wiki/Abstract_factory_pattern и документации для ADO.NET DbProviderFactories на http://msdn.microsoft.com/en-us/library/wda6c36e.aspx для реальной реализации.
Ответ 3
На основе вашего примера вы выполняете ACB, вы хотите выполнить ABC.
Чтобы запустить код после дочернего конструктора, вам нужно сделать вызов после B (дочерний конструктор), вы не можете вызвать код в (родительский конструктор), тогда вы не сможете выполнить ABC.
Переместить DoThisAutomaticallyAfterConstruction()
в конец конструктора дочернего класса?
Правда, странный вопрос.
Ответ 4
В то время как ответ @Jeremy Todd (принятый) работает и является широко распространенным решением проблемы, у него есть недостаток: не очень IoC и сериализация, поскольку ваш класс не может быть правильно сконструирован с использованием new
. Позвольте мне представить общее решение, используя некоторые функции С#. Обратите внимание, что это решение не требует использования шаблона 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
, он будет подавлять любые вызовы, не сделанные из фактического класса.