views:

422

answers:

5

I understand that it is used to deallocate unmanaged resources, however, I am confused as to when Dispose is actually called. I know it is called at the end of a using block, but does it also get invoked when the object is garbage collected?

+2  A: 

Dispose() is called at the end of the Using block so that you can count on the Dispose actions (e.g. closing a db connection) to take place as the object goes out of scope. Simple as that.

Update: there is nothing specific to unmanaged resources in a call to Dispose (although that is a common use).

Update 2: there is a bit of a debate on the thread that Reed Copsey started that is useful for understanding IDisposable. I highly recommend this article for people wanting to know more.

In a nutshell, an IDisposable class allows you to explicitly handle the deallocation of resources (typically unmanaged resources or database connections) via the Dispose() method. IDisposable class instances should be created within a "Using" block so as to ensure that the Dispose method is actually called. If you fail to do this (or to call it explicitly in a "finally" block, etc.) then your Dispose method will not be called and you'll orphan the objects you'd like to clean up. In all cases, I place Disposable classes in Using blocks and you should too.

As an alternative you can handle the clean up of resources in a Finalizer (a class Destructor). This will be called automatically when the class is GC'd. The disadvantages of this approach are that you will not explicitly control when objects are cleaned up and there are some threading issues to contend with. Thus, for example, problems in Destructors are very difficult to debug due to the fact that they are called asynchronously and in a different thread. The only advantage is that you don't have to remember to call your Destructor like you do Dispose. Of course, if you always use Using blocks, this is a non-issue.

NOTE: I ktrauberman, Reed and Pontus have made some good points about how you can get around the points I make below. This is what I do, but I cannot argue that this is the only way to do things. Indeed, Microsoft even recommends calling Dispose() from your Finalizer in some instances. However, I'll leave the discussion here just as an illustration of why it is important to follow Reed's advice re: being careful when mixing Destructors and Dispose().

Where I disagree with the Reed's answer is in the assertion that you should implement an IDisposable class by calling Dispose() within your Finalizer. This just conflates the two different constructs and may well lead to problems. For example, if you do create your IDisposable class instance in a Using block and you call Dispose() in the Destructor, it will be called twice - with potentially nasty and difficult to debug crashes (again - you don't control the timing of GC). (Side note: this is actually true of Destructors in general - they can, in certain circumstances be called more than once!)

Mark Brittingham
Mark, The article you link to contradicts your position. It says on page 3 "Microsoft recommends that you implement both Dispose and Finalize when working with unmanaged resources." On page 4, it also shows an example where it calls Dispose() from the destructor.
Kyle Trauberman
I don't think you understand completely what GC.SupressFinalize() does. You call this at the end of your Dispose() method, so if the developer calls Dispose() the Garbage collector never runs the finalizer, so cleanup only occurs once.
Kyle Trauberman
kt - thanks for the debate. I don't mix the two but, as I say above, I have to admit your point and do so gladly. Debates on SO are always good for pushing the envelope of what we know.
Mark Brittingham
Exactly - this is the position of the Framework Design Guidelines 2nd Edition, as well. They mention some concrete examples of why it's important, too.
Reed Copsey
Mark, I appreciate the debate as well. I think you should do some more research, however. Only using one or the other method can lead to code that is not very friendly, when it comes to memory management.
Kyle Trauberman
Reed posted a good comment on his answer. "It's difficult (and I don't think good) to enforce usage patterns on the end user, and it's easy enough to handle both cases."
Kyle Trauberman
Yeah - I'm coming around to your position (both of you). That's why I removed the original downvote on Reed's answer and gave an upvote instead.
Mark Brittingham
This is why it is important to keep an open mind when developing software. You never know when you'll learn something unexpected. :)
Kyle Trauberman
+2  A: 

Dispose is called in a few places:

  1. At the end of a using block.
  2. When explicitly called (in a try{} finally{} for instance.)

It is recommended that you call it yourself when you are done with a resource, to better manage resources.

EDIT: I was mistaken. Dispose is NOT called during garbage collection. See this article.

Kyle Trauberman
Dispose() is not automatically called during garbage collection.
Brian Rasmussen
+1 for Brian's comment.
Jon Skeet
Just edited. I did some research after posting (that I should have done beforehand) my bad.
Kyle Trauberman
A: 

See this previous question: Proper use of the IDisposable interface

Michael Kniskern
+2  A: 

No it does not get called when the object is garbage collected. If you want that behavior you can use the destructor (finalizer) and the call Dispose() from there.

As you say it is automatically called and the end of a using block.

Brian Rasmussen
+9  A: 

If you implement IDisposable correctly, you should also include a finalizer that will call Dispose() on your object.

If you do that, it will get called by the GC. However, it's still a VERY good idea to try to always dispose of these objects yourself.

The biggest problem with relying on the finalizer to call Dispose is that it will happen in another thread which you don't control. This can have nasty consequences in certain situations, including causing an exception that's happening in the GC thread, which is not good, as well as having a disposed field you check. This is also part of why including GC.SuppressFinalize(this) in your Dispose() method is important - once an object's disposed, you don't want to re-dispose it.

Reed Copsey
Sorry, Mark, but C# garbage collection isn't the magic wand you're implying. If an object implements IDispose, you should dispose it explicitly, preferably through using. Your advice is quite right for other objects though -- but non-memory resources are dangerous!
Pontus Gagge
If the user never calls Dispose(), and they don't use a using block, any unmanaged resources will never get cleaned. Here it depends on why your class is implementing IDiposable, but the question explicitly stated "to deallocate unmanaged resources". Finalizers exist for exactly this reason.
Reed Copsey
I'm not sure that we disagree Pontus; I create IDisposable classes specifically for use in a Using block. If not then you would indeed need to call it explicitly to free up unmanaged resources, release connections, etc.
Mark Brittingham
Mark, Take a look at the article I linked in my answer. It gives a great example of calling Dispose() from the finalizer safely.
Kyle Trauberman
Also, why would you not want to use both methods? What happens if you allow explicit Dispose() and NOT a finalizer? If the dev never calls Dispose() for whatever reason, then you have a memory leak. If you allow both, you solve this problem.
Kyle Trauberman
kt - I know that you *can* call Dispose() from a Finalizer I just don't think it wise to do so. If you create your IDisposable class instance in a Using block, you'll end up calling Dispose() twice and, without proper care, will end up with a nasty, difficult to diagnose bug.
Mark Brittingham
I agree with you, you have to use it properly - incorporate a check to ensure you don't run the logic twice, and call GC.SuppressFinalize() so the GC doesn't run on the instance once its already been disposed of.
Kyle Trauberman
-cont- Honestly, if you are worried that you might screw up and forget to call Dispose() then you should just put your cleanup code in your Destructor (Finalizer). If it is Ok there anyway, why court trouble with IDisposable? To sum: AFAIK best practices says one or the other - not both.
Mark Brittingham
See my comments on your answer.
Kyle Trauberman
Mark: In general I agree. If you're writing code other people will use, I don't. It's difficult (and I don't think good) to enforce usage patterns on the end user, and it's easy enough to handle both cases. Note the end of my post -I explicitly say you need to be careful (field + GC.Sup...)
Reed Copsey
kt - see my comments in the actual question. I have to admit that you are right about the MS recommendation. I don't *like* the advice but I cannot argue that best practice calls for one or the other when Microsoft says differently. Thus, I will admit that I was wrong.
Mark Brittingham
Reed - I've deleted comments to the effect that you were wrong. Please accept my apologies.
Mark Brittingham
Reed Copsey
Mark - Thanks for the great discussion :) I love things that make me think and go read to make sure I'm not blowing smoke!
Reed Copsey
Mark Brittingham
BTW - I removed my original downvote and have replaced it with an upvote.
Mark Brittingham