如何处理具有异步方法的对象?

我有这个对象PreloadClient实现IDisposable ,我想处理它,但在异步方法完成他们的调用后……这没有发生

  private void Preload(SlideHandler slide) { using(PreloadClient client = new PreloadClient()) { client.PreloadCompleted += client_PreloadCompleted; client.Preload(slide); } // Here client is disposed immediately } private void client_PreloadCompleted(object sender, SlidePreloadCompletedEventArgs e) { // this is method is called after a while, // but errors are thrown when trying to access object state (fields, properties) } 

那么,任何想法或工作?

  1. 您不应该使用using构造,而是在不再需要它们时处置它们:

     // keep a list of strong references to avoid garbage collection, // and dispose them all in case we're disposing the encapsulating object private readonly List _activeClients = new List(); private void Preload(SlideHandler slide) { PreloadClient client = new PreloadClient(); _activeClients.Add(client); client.PreloadCompleted += client_PreloadCompleted; client.Preload(slide); } private void client_PreloadCompleted(object sender, SlidePreloadCompletedEventArgs e) { PreloadClient client = sender as PreloadClient; // do stuff client.PreloadCompleted -= client_PreloadCompleted; client.Dispose(); _activeClients.Remove(client); } 
  2. 在这种情况下,您必须在处置主类时处置所有客户端:

     protected override Dispose(bool disposing) { foreach (PreloadClient client in _activeClients) { client.PreloadCompleted -= client_PreloadCompleted; client.Dispose(); } _activeClients.Clear(); base.Dispose(disposing); } 
  3. 请注意,此实现不是线程安全的

    • 访问_activeClients列表必须是线程安全的 ,因为从另一个线程调用PreloadCompleted方法
    • 您的包含对象可能会在客户端触发事件之前处理。 在这种情况下,“做东西”应该什么也不做,所以这是你应该照顾的另一件事。
    • 在事件处理程序中使用try / finally块可能是个好主意,以确保在所有情况下都可以处置该对象

为什么不在回调中处置客户端?

我有一些想法:

  1. 改变你的架构。
  2. 处理在处理程序中
  3. 使用EventWaitHandle

如果正在注册事件处理程序,则在有可能在其上调用的事件时,您无法真正处置该对象。 最好的办法是使包含类一次性使用并将客户端存储在类变量中,以便在包含类时进行处理。

就像是

 class ContainingClass : IDisposable { private PreloadClient m_Client; private void Preload(SlideHandler slide) { m_Client = new PreloadClient()) m_Client.PreloadCompleted += client_PreloadCompleted; m_Client.Preload(slide); } private void client_PreloadCompleted(object sender, SlidePreloadCompletedEventArgs e) { } public void Dispose() { if (m_Client != null) m_Client.Dispose(); } } 

好吧,处理一个对象用于杀死你不想要的资源,直到GC(最终)到来并收集你的对象。 您的dispose方法是否会在client_PreloadCompleted杀死您需要的任何内容?

当所有预期的回调都发生时,您可以让对象自行处理:为您期望的每个回调保留一个“引用计数器”,并在每次回调时减少该回调 – 在回调处理程序结束时检查为null,如果是,则进行处理。

其他解决方法:不要担心IDisposable 。 GC将收集您的对象。 您可能不希望回调处理程序(可能不会被触发)具有临界状态。 它(回调)应该在调用它时打开它需要的任何资源然后关闭它们。

异步等待和确定性处理不能很好地混合。 如果你能找到一种方法来分割代码,使得一次性东西进入一个类,而事件进入另一个类,这将使一切变得更简单。

为什么不在client_PreloadCompleted方法中进行处理? 类似于thecoop提供的内容,只需使用上述方法中的Dispose调用,就可以从客户端对象中访问所有需要的数据。

编辑:我认为这也是orialmog提供的。