views:

437

answers:

7

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 ??

+1  A: 

I have a few ideas:

  1. change your architecture.
  2. dispose in the handler
  3. use EventWaitHandle
Krzysztof Koźmic
check this out about EventWaitHandle : http://wekempf.spaces.live.com/Blog/cns!D18C3EC06EA971CF!672.entry?sa=571623833
jalchr
all good points
almog.ori
+2  A: 

Why not dispose the client in the callback?

almog.ori
If you have 2 or more events ... which one you pick to dispose :) ?
jalchr
@jalchr: If you implemented "Dispose" *correctly*, calling dispose more than once does not affect the outcome no matter how many time "Dispose" is called.
Sung Meister
A: 

If there are event handlers being registered, you can't really dispose the object while there are events that could be called on it. Your best bet is to make the containing class disposable & store the client in a class variable, to be disposed when the containing class is.

Something like

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();
    }
}
thecoop
Bad idea. Now you tied lifetime of containingclass with lifetime of client. what If Preload is called more than once? You won't dispose client. What if you call dispose on container before async operation completes? You'll most likely get ugly exception
Krzysztof Koźmic
A: 

Well, disposing an object is used to kill resources that you don't want held until the GC (eventually) comes and collects your object. Does your dispose method kill anything you need in client_PreloadCompleted?

You could have the object dispose itself, when all expected callbacks have happened: Keep a "reference counter" for each callback you are expecting and decrement that on each callback that happens - check for null at end of callback handler and dispose if so.

Other workaround: Don't worry about IDisposable. GC will collect your object. You probably don't want a callback handler (that might not be fired) to have critical state. It (the callback) should just open any resources it needs when it is called and close them then.

Daren Thomas
A: 

Asynchronous waits and deterministic disposal don't mix very well. If you can find a way of splitting the code such that the disposable stuff goes in one class and the events go in another, that would make everything simpler.

Christian Hayter
Could you please elaborate more ... what do you mean by putting events in another class ... ?
jalchr
Take a hypothetical class that holds state which requires deterministic cleanup. That class is a good candidate for IDisposable. However, if you call an asynchronous method on it, you then have to wait for the asynchronous method to complete before you can dispose it. Why bother? You might as well make it a synchronous method and dispose the object when the method completes. You have accomplished the same thing in the same amount of time.
Christian Hayter
A: 

Why not disposing in the client_PreloadCompleted method? Similar to what thecoop offered, just with the Dispose call inside the above method, after you have accessed all the needed data from inside the client object.

Edit: I think that is what orialmog offered as well.

Noam Gal
+3  A: 

1) You don't have to put it inside using construct, but rather dispose it manually.

 // we have a list of strong reference to avoid garbage collection,
 // and to 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
  • 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.
Groo
I believe this is a great answer
jalchr