Search code examples
c#.netasynchronousdisposeidisposable

How to dispose objects having asynchronous methods called?


I have this object PreloadClient which implements IDisposable, I want to dispose it, but after the asynchronous methods finish their call... which is not happening

    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)
    }

So, any ideas or work arounds ??


Solution

    1. You shouldn't use the using construct, but rather dispose your objects when they are no longer needed:

      // 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<PreloadClient> _activeClients = new List<PreloadClient>();
      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. in this case, you have to dispose all clients when disposing the main class:

      protected override Dispose(bool disposing)
      {
          foreach (PreloadClient client in _activeClients)
          { 
              client.PreloadCompleted -= client_PreloadCompleted;
              client.Dispose();
          }
          _activeClients.Clear();
          base.Dispose(disposing);
      }
      
    3. Note that this implementation is not thread safe

      • Access to the _activeClients list must be made thread-safe, as your PreloadCompleted method is called from a different thread
      • Your containing object may be disposed before a client fires the event. In that case "do stuff" should do nothing, so this is another thing you should take care of.
      • It might be a good idea to use a try/finally block inside your event handler, to make sure that the object gets disposed in all cases