9

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?

Tony the Pony
  • 40,327
  • 71
  • 187
  • 281
  • Sorry, Mark, but you're barking up the wrong tree, possibly in the wrong forest. Look at the references in the other answers if you don't believe me. – Pontus Gagge Mar 10 '09 at 21:28
  • 1
    Wow - why would someone vote to close? This is a great debate!! – Mark Brittingham Mar 10 '09 at 22:52
  • I'll have to take back my earlier assertion: MS says that a Dispose/Finalize pair is Ok. Personally, I don't like this approach for the reasons outlined below but ktrauberman has found a reference that proves that I should not be so unyielding on this point...so I yield. – Mark Brittingham Mar 10 '09 at 23:01

4 Answers4

11

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
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 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 Mar 10 '09 at 21:26
  • 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 Mar 10 '09 at 21:33
  • 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 Mar 10 '09 at 22:08
  • 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 Mar 10 '09 at 22:28
  • 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 Mar 10 '09 at 22:35
  • 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 Mar 10 '09 at 22:46
  • 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 Mar 10 '09 at 22:49
  • -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 Mar 10 '09 at 22:51
  • See my comments on your answer. – Kyle Trauberman Mar 10 '09 at 22:52
  • 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 Mar 10 '09 at 23:02
  • 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 Mar 10 '09 at 23:03
  • Reed - I've deleted comments to the effect that you were wrong. Please accept my apologies. – Mark Brittingham Mar 10 '09 at 23:05
  • BTW - From MS Design Guidelines 2nd ed: "Do implement the Basic Dispose Pattern and provide a finalizer on types holding resources that need to be freed explicitly and that do not have finalizers." Cwalina & Abrams recommend doing both. – Reed Copsey Mar 10 '09 at 23:11
  • 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 Mar 10 '09 at 23:39
  • Me too - I thought I had everything down WRT IDisposable and have been using it for awhile. But I learned quite a bit more tonight trying to defend my position. BTW - I'm still working on removing comments - SO keeps telling me to wait 30 seconds between deletes (& then get distracted, etc.). – Mark Brittingham Mar 11 '09 at 00:07
  • BTW - I removed my original downvote and have replaced it with an upvote. – Mark Brittingham Mar 11 '09 at 00:08
3

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
  • 25,414
  • 13
  • 85
  • 121
2

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
  • 28,545
  • 12
  • 80
  • 110
  • 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 Mar 10 '09 at 22:46
  • 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 Mar 10 '09 at 22:47
  • 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 Mar 10 '09 at 23:08
  • 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 Mar 10 '09 at 23:12
  • 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 Mar 10 '09 at 23:18
  • 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 Mar 10 '09 at 23:19
  • 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 Mar 11 '09 at 00:05
  • 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 Mar 11 '09 at 04:26
2

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
  • 114,645
  • 34
  • 221
  • 317