views:

208

answers:

7

I have a few basic questions about the Dispose Pattern in C#.

In the following code snippet, which seems to be a standard way of implementing the dispose pattern, you’ll notice that managed resources are not handled if disposing is false. How/when are they handled? Does the GC come along and handle the managed resources later? But if that’s the case, what does the GG.SuppressFinalize(this) call do? Can someone give me an example of disposing of managed resources? Unhooking events comes to mind. Anything else? The way the pattern is written, it seems they would get disposed (later) if you did nothing in the “if (disposing)” section. Comments?

protected virtual void Dispose(bool disposing)
{  
    if (!disposed)
    {
        if (disposing)
        {
            // Dispose managed resources.
        }

        // There are no unmanaged resources to release, but
        // if we add them, they need to be released here.
    }
    disposed = true;

    // If it is available, make the call to the
    // base class's Dispose(Boolean) method
    base.Dispose(disposing);
}
// implements IDisposable
public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

Is it true what I read about locks in Dispose(bool) in this thread, http://stackoverflow.com/questions/1196203/how-do-i-implement-the-dispose-pattern-in-c-when-wrapping-an-interop-com-object? It says, “Meta-meta comment - as well as that, it's important that you never acquire locks or use locking during your unmanaged cleanup.” Why is that? Does it apply to unmanaged resources as well?

Finally, does on ever implement a finalizer (~MyClass() in C#) without implementing IDisposable? I believe I read somewhere that finalizers and IDisposable are not necessary (or desirable) if there are no unmanaged resources. However, I do see the use of a finalizer without IDisposable in some examples (see: http://www.codeproject.com/KB/cs/idisposable.aspx as one example) Thanks, Dave

+4  A: 

This way of implementing the IDisposable pattern is a fail-safe way: In case a client forgets to call Dispose, the finalizer called by the runtime will call Dispose(false) later (Note that this part is missing in your sample).

In the latter case, i.e. when Dispose is called by the finalizer, managed resources will already have been cleaned up because otherwise the object in question would not have been eligible for garbage collection.

But if that’s the case, what does the GC.SuppressFinalize(this) call do?

Running the finalizer comes with additional costs. Therefore it should be avoided if possible. Calling GC.SuppressFinalize(this) will skip running the finalizer and therefore the object can be garbage collected more efficiently.

In general one should avoid relying on finalizers as there is no guarantee that a finalizer will run. Some of the problems with finalizers are described by Raymond Chen in the following post:

When do I need to use GC.KeepAlive?

0xA3
A small nitpick: *"In the latter case ... managed resources will already have been cleaned up"*. This isn't necessarily the case: GC is non-deterministic so maybe they'll have been cleaned up, maybe not. Either way, you should behave *as if* they've been cleaned up (ie, you shouldn't attempt to do anything with them).
LukeH
+2  A: 

...you’ll notice that managed resources are not handled if disposing is false. How/when are they handled?

You didn't include it in your sample, but often the type will have a destructor which will call Dispose(false). Thus, when disposing is false, you "know" that you're in a finalizer call, and thus should *not* access any managed resources, because they might have already been finalized.

The GC finalization process only ensures that finalizers are invoked, not the order that they're invoked in.

what does the GG.SuppressFinalize(this) call do?

It prevents the GC from adding your object to the finalization queue and eventually calling object.Finalize() (i.e. your destructor). It's a performance optimization, nothing more.

The way the pattern is written, it seems they would get disposed (later) if you did nothing in the “if (disposing)” section

Maybe; it depends upon how the type is written. A major point to the IDisposable idiom is for "deterministic finalizaion" -- saying "I want your resources freed now" and having it mean something. If you "ignore" the disposing=true block and don't "forward" the Dispose() call, one of two things will happen:

  1. If the type has a finalizer, the finalizer for the object may eventually be invoked sometime "later".
  2. If the type doesn't have a finalizer, the managed resource will "leak," as Dispose() will never be invoked on them.

it's important that you never acquire locks or use locking during your unmanaged cleanup.” Why is that? Does it apply to unmanaged resources as well?

It's a sanity issue -- YOUR sanity. The simpler your cleanup code, the better, and it's always a good idea to not throw exceptions from Dispose(). Using locks can result in exceptions or deadlocks, either of which are good ways to ruin your day. :-)

does on ever implement a finalizer (~MyClass() in C#) without implementing IDisposable

One could, but it would be considered bad style.

jonp
+1  A: 

The normal way for objects to be disposed is by calling it's Dispose() method. When done that way, the SuppressFinalize call removes the object from the finalizer queue, turning it into a regular managed object that can easily be garbage collected.

The finalizer is only used when the code fails to dispose the object properly. Then the finalizer calls Dispose(false) so that the object can at least try to clean up unmanaged resources. As any managed objects that the object references may already have been garbage collected at this stage, the object should not try to clean up them.

Guffa
A: 

Instead of trying to learn about disposition via the pattern, you might want to flip things around and try learning why the pattern is implemented this way based on CLR fundamentals and the intended usage of the IDisposable interface. There's a very good introduction to this that should answer all your questions (and a few you didn't think to ask) at http://msdn.microsoft.com/en-us/magazine/cc163392.aspx.

Nicole Calinoiu
+2  A: 

The pattern described above was a matter of dealing eloquently with the overlapping concerns of disposal and finalisation.

When we are disposing, we want to:

  1. Dispose all disposable member objects.
  2. Dispose the base object.
  3. Release unmanaged resources.

When finalising we want to:

  1. Release unmanaged resources.

Added to this are the following concerns:

  1. Disposal should be safe to call multiple times. It should not be an error to call x.Dispose();x.Dispose();
  2. Finalisation adds a burden to garbage collection. If we avoid it if we can, specifically if we have already released unmanaged resources, we want to suppress finalisation as it is no longer needed.
  3. Accessing finalised objects is fraught. If an object is being finalised, then any finalisable members (which would also be dealing with the same concerns as our class) may or may not have already been finalised and will certainly be on the finalisation queue. As these objects will likely also be managed disposable objects, and as disposing them will release their unmanaged resources, we do not want to dispose of them in such a case.

The code you give will (once you add in the finaliser that calls Dispose(false) manage these concerns. In the case of Dispose() being called it will clean up both managed and unmanaged members and suppress finalisation, while also guarding against multiple calls (it is not however thread-safe in this regard). In the case of the finaliser being called, it will clean up unmanaged members.

However, this pattern is only required by the anti-pattern of combining managed and unmanaged concerns in the same class. A much better approach is to handle all unmanaged resources through a class which is concerned only with that resource, whether SafeHandle or a separate class of your own. Then you will have one of two patterns, of which the latter will be rare:

public class HasManagedMembers : IDisposable
{
   /* more stuff here */
   public void Dispose()
   {
      //if really necessary, block multiple calls by storing a boolean, but generally this won't be needed.
      someMember.Dispose(); /*etc.*/
   }
}

This has no finaliser and doesn't need one.

public class HasUnmanagedResource : IDisposable
{
  IntPtr _someRawHandle;
  /* real code using _someRawHandle*/
  private void CleanUp()
  {
     /* code to clean up the handle */
  }
  public void Dispose()
  {
     CleanUp();
     GC.SuppressFinalize(this);
  }
  ~HasUnmanagedResource()
  {
     CleanUp();
  }
}

This version, which will be much rarer (not even happening in most projects) has the disposal deal solely with dealing with the sole unmanaged resource, for which the class is a wrapper, and the finaliser doing the same if disposal didn't happen.

Since SafeHandle allows for the second pattern to be handled for you, you shouldn't really need it at all. In any case, the first example I give will handle the vast majority of cases where you need to implement IDisposable. The pattern given in your example should only be used for backwards compatibility, such as when you derive from a class that uses it.

Jon Hanna
A: 

If your class will hold unmanaged resources directly, or if it might ever be inherited by a descendant class that will do so, Microsoft's dispose pattern will provide a good way to tie together the finalizer and disposer. If there's no realistic possibility that either your class or its descendants will ever hold unmanaged resources directly, you should delete the template code and simply implement Dispose directly. Given that Microsoft has strongly recommended that unmanaged resources be wrapped in classes whose sole purpose is to hold them(*) (and has classes like SafeHandle for precisely that purpose) there's really no need for the template code anymore.

(*) Garbage collection in .net is a multi-step process; first the system determines which objects aren't referenced anywhere; then it makes a list of Finalize'able objects which aren't referenced anywhere. The list, and all objects on it, will be re-declared "live", which will mean all objects referred to by them will also be live. At that point, the system will perform an actual garbage collection; it then will run all the finalizers on the list. If an object holds, e.g. a direct handle to a font resource (unmanaged) as well as references to ten other objects which in turn hold direct or indirect references to a hundred more objects, then because of the unmanaged resource the object will need a finalizer. When the object comes due for collection, neither it nor the 100+ objects to which it holds direct or indirect references will be eligible for collection until the pass after its finalizer runs.

If instead of holding a direct handle to the font resource, the object held a reference to an object which holds the font resource (and nothing else), the latter object would need a finalizer, but the former one would not (since it doesn't hold a direct reference to the unmanaged resource. Only one object (the one which held the finalizer), rather than 100+, would have to survive the first garbage collection.

supercat
+2  A: 

Nobody got to the last two questions (btw: ask only one per thread). Using a lock in Dispose() is pretty lethal to the finalizer thread. There's no upper-bound on how long the lock will be held, your program will crash after two seconds when the CLR notices that the finalizer thread got stuck. It is very unlikely the lock will clear, managed threads are suspended. Moreover, it is just a bug. You should never call Dispose() when another thread might still have a reference to the object.

Yes, implementing a finalizer without implementing IDisposable is not unheard of. Any COM object wrapper (RCW) does that. So does the Thread class. This was done because it just isn't practical to call Dispose(). In the case of a COM wrapper because it is just not possible to keep track of all reference counts. In case of Thread because having to Join() the thread so that you could call Dispose() defeats the purpose of having a thread.

Pay attention to Jon Hanna's post. Implementing your own finalizer is indeed wrong 99.99% of the time. You've got the SafeHandle classes to wrap unmanaged resources. You'd need something pretty obscure to not be wrappable by them.

Hans Passant