c#在对象构造函数中启动异步方法 - 不好的做法?

gho*_*ago 25 .net c# asynchronous

我在一个类似的对象构造函数中有一些代码

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);
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 这通常被认为是不好的做法吗?
  2. 在我的类中定义一个start方法(用户可以调用它来执行异步操作)会更好(或更好)吗?

这个答案给我的印象是,将它留给用户是不好的做法,虽然我特别谈到在构造函数中启动异步方法,而不是正确构造对象.

Jer*_*xon 22

这可以通过稍微不同的方法轻松完成.在所有现实中,这种情况一直在发生,不是吗?这是一个简单的解决方案,为您提供一个选项,而不做一些愚蠢的事情:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是我用于所有ViewModel的模式 (4认同)

Tej*_*ejs 14

在构造函数中做一些与创建对象实例没有直接关系的东西通常不是一个好主意.例如,如果我不知道您的MyObject类的目的,我可能不知道它在创建新实例时会产生异步进程.这通常是一种不好的做法.

这几乎听起来像你在说; 嘿,创建这个对象,它只在这个异步过程完成后才可用; 这是违反直觉的.

如果你想做这样的事情,我认为通过工厂模式你肯定会得到更好的服务; 你这样叫一个工厂,它会创建对象并为你调用方法:

 var instance = MyObjectBuilder.CreateInstance();

 // Internally, this does
 //    var x = new MyObject();
 //    x.Initilizatize();
 //    return x;
Run Code Online (Sandbox Code Playgroud)

如果你的对象在完成初始化之前不可用,那么你应该暴露一个属性来检查它是否准备就绪,如下所示:

 instance.WaitForReady(); // blocking version
 if(instance.IsReady) // non blocking check
Run Code Online (Sandbox Code Playgroud)


Dus*_*vis 8

是的,因为您可能会引入意外行为.应该构造对象,不受异常和意外行为的影响.如果在构造对象并且用户试图使用它之后仍然发生异步操作...

另外,如果异步方法仍在运行并且用户决定销毁该对象,该怎么办?

好读:http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613/ref=sr_1_1?s= books& ie= UTF8& qid= 1314813265& sr=1-1

如果你的异步方法必须是异步的,因为它是一个长时间运行的进程,那么它需要被移动到它自己的方法,由用户调用,而不是在对象被实例化时.

如果用户在异步方法完成之前决定销毁它,这也意味着你可能有内存泄漏,那么你也有可能保持对该对象的引用.

如果要使用工厂或构建器创建初始化对象的HAS.

  • @tsells这不是一个问题,它是否可以正确完成或完成,但如果在最佳实践的背景下这是不好的做法,答案是肯定的,这是一个不好的做法.不是异步方法为UI提供什么好处的问题.如果要以这种方式执行此操作,则应使用工厂,而不是在构造函数中执行此操作. (2认同)

Nic*_*oiu 5

在完全构造实例之前,在构造函数中启动任何可能在另一个线程上调用实例的东西绝对是个坏主意.即使它的最后一行构造函数,子类构造函数完成运行前,因为子类构造函数的代码基类的构造函数代码后运行回调可能被调用.即使您的课程今天已被密封,未来也可能未开封,如果将来添加子类,追踪此类问题很可能会非常昂贵.

基本上,如果你不想最终打猎heisenbugs,不要从构造函数中做过这类事情.相反,添加一个像你在问题#2中提到的"开始"方法.


Sal*_*iti 5

我同意,不好主意.添加Initialize方法.您还可以添加一个返回新对象的静态方法并执行Initialize方法.通过这种方式,您还可以将构造函数保持为私有.

public class XXX
{
    private XXX()
    {
    }

    private void DoMyWeirdThingsAsynchronously()
    {
        ....
    }

    public static XXX PerformSomethingStrange()
    {
        XXX result = new XXX();
        result.DoMyWeirdThingsAsynchronously();
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我真的很喜欢这个建议.它与Tejs基本相同,但通过包含私有构造函数更清楚一点.此外,即使它不使用它很容易就能使用的async/await关键字(2011年给出了答案),我认为这是一个比JNixons更好的设计选项,因为它封装了幕后异步初始化的责任.而不是要求调用者"知道"它(即构造一个实例,然后调用异步初始化方法). (3认同)