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

С# начать асинхронный метод в конструкторе объекта - плохая практика?

У меня есть некоторый код в конструкторе объекта, аналогичном

delegate DataSet MyInvoker;

public MyObject(Param1 p1)
{
    // property sets here
    // ...

    BeginMyAsyncMethod();
}

public void BeginMyAsyncMethod() 
{
    // set some properties
    // ...

    MyInvoker inv = new MyInvoker(SomeBeginMethod);
    inv.BeginInvoke(new AsyncCallback(SomeEndMethod), null);
}

Мои вопросы:

  • Это обычно считается плохой практикой?
  • Будет ли лучше (или хорошей) практикой определять метод начала в моем классе, который пользователь будет вызывать для выполнения асинхронного действия?

Этот ответ дает мне впечатление, что оставить его пользователю - это плохая практика, хотя я говорю конкретно о запуске async-методов в конструкторе, а не о правильной конструкции объекта.

4b9b3361

Ответ 1

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

public class MyResource
{
    // do this instead of a constructor
    public async Task<MyResource> StartAsync()
    {
        await Task.Delay(1);
        return this;
    }
}

public class MyControl
{
    public MyResource Resource { get; set; }
    async void Button_Click(object s, EventArgs e)
    {
        // call start as if it is a constructor
        this.Resource = await new MyResource().StartAsync();
    }
}

Ответ 2

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

Похоже, вы говорите; эй, создайте этот объект, и он будет полезен только тогда, когда этот асинхронный процесс будет завершен; что противоречит интуиции.

Если вы хотите сделать что-то подобное, я бы подумал, что вам будет определенно лучше служить, выбрав шаблон factory; вы вызываете factory так, и он создаст объект и вызовет метод для вас:

 var instance = MyObjectBuilder.CreateInstance();

 // Internally, this does
 //    var x = new MyObject();
 //    x.Initilizatize();
 //    return x;

Если ваш объект не будет использоваться до завершения его инициализации, вы должны, вероятно, открыть свойство, чтобы проверить, готово ли оно:

 instance.WaitForReady(); // blocking version
 if(instance.IsReady) // non blocking check

Ответ 3

Да, потому что вы потенциально представляете неожиданное поведение. Объекты должны быть построены и свободны от исключений и неожиданного поведения. Если асинхронные действия все еще происходят после создания объекта и пользователь пытается его использовать...

Кроме того, что, если метод async все еще запущен, и пользователь решает уничтожить объект?

Хорошо читать: http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613/ref=sr_1_1? S = книги и усилитель, то есть = UTF-8 & QID = 1314813265 & ср = 1-1

Если ваш асинхронный метод должен быть асинхронным, потому что это длительный процесс, тогда его нужно переместить в свой собственный метод, который вызывается пользователем, а не при установке объекта.

У вас также есть потенциальная проблема сохранения ссылки на объект, если пользователь решает уничтожить его до того, как будет выполнен асинхронный метод, что означает, что у вас может быть утечка памяти.

Если это необходимо для инициализации объекта, следует создать с помощью factory или строителя.

Ответ 4

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

В принципе, если вы не хотите заканчивать охоту на heisenbugs, никогда не делайте такого рода вещи у конструктора. Вместо этого добавьте метод "start", как вы упомянули в своем вопросе №2.

Ответ 5

Я согласен, плохая идея. Добавьте метод Initialize. Вы также можете добавить статический метод, который возвращает новый объект и выполняет метод Initialize. Таким образом, вы также можете сохранить конструктор как закрытый.

public class XXX
{
    private XXX()
    {
    }

    private void DoMyWeirdThingsAsynchronously()
    {
        ....
    }

    public static XXX PerformSomethingStrange()
    {
        XXX result = new XXX();
        result.DoMyWeirdThingsAsynchronously();
        return result;
    }
}

Ответ 6

Лично я бы не стал помещать такой код в конструктор. Было бы лучше предоставить конкретный метод для запуска асинхронной операции. Выбрасывание исключений из конструктора (кроме исключений проверки) - плохая идея.