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

Есть ли способ автоматического вызова определенного метода сразу после запуска всех конструкторов?

Я хочу, чтобы иметь возможность вызвать конкретный метод автоматически при построении производного объекта, однако я не могу думать, как это сделать. Следующий код иллюстрирует. Другой ответ рекомендовал 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
    }
}
4b9b3361

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