Есть ли способ автоматически вызывать определенный метод сразу после запуска всех конструкторов? - PullRequest
19 голосов
/ 03 ноября 2011

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

Ответы [ 4 ]

17 голосов
/ 03 ноября 2011

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

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

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();
4 голосов
/ 03 ноября 2011

Звучит как хороший кандидат на завод.Сделайте все конструкторы частными или защищенными, требуя, чтобы потребители вашего кода вызывали фабричный метод, когда им нужен экземпляр вашего объекта.В фабричном методе вы используете оператор new для создания объекта, а затем вызываете DoThisAutomaticallyAfterConstruction() перед возвратом объекта.

EDIT

Фабрика может быть статическим методом, илиу вас может быть заводской объект.См., Например, Википедию по шаблону абстрактной фабрики на http://en.wikipedia.org/wiki/Abstract_factory_pattern, и документацию для ADO.NET DbProviderFactories на http://msdn.microsoft.com/en-us/library/wda6c36e.aspx для реальной реализации.

3 голосов
/ 29 июля 2016

Хотя (принятый) ответ @ Джереми Тодда работает и является широко распространенным решением проблемы, у него есть недостаток: он не очень удобен для IoC и сериализации, поскольку ваш класс не может быть правильно сконструирован с использованием new. Позвольте мне представить общее решение с использованием некоторых возможностей 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, он будет подавлять любые вызовы, не сделанные из фактического класса.

2 голосов
/ 03 ноября 2011

На основании вашего примера вы выполняете ACB , вы хотите выполнить ABC .

Чтобы запустить код после дочернего конструктора, вам нужно сделатьвызов после B (дочерний конструктор), вы не можете вызвать код в A (родительский конструктор), тогда вы не сможете выполнить ABC.

Переместить DoThisAutomaticallyAfterConstruction() в конец конструктора дочернего класса?

Действительно странный вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...