11

I have a number of classes which have private member variables that implement IDisposable (timers, brushes, etc). Do I need to do anything to ensure these variables are cleaned up properly by the .NET Framework?

The literature I've come across is referring to "managed resources" vs. "unmanaged resources". These terms are confusing to me because you can have a managed class which implements functionality using unmanaged resources. Is that considered an "unmanaged resource" or "managed resource" ?

My understanding is if you aren't calling Dispose() on an object that implements IDisposable, then the resources aren't being freed until the application exits. This situation could cause OutOfMemory exceptions when running the program for a long period of time.

How can I be sure my code is handling resource management correctly? It's important for these objects because they are custom controls and there may be a lot of drawing which consumes IDisposable resources. I use the C# using statement whenever I can, but sometimes I need to make an object implementing IDisposable a member variable, and the using statement won't help me there.

Trevor Balcom
  • 3,766
  • 2
  • 32
  • 51
  • FYI - If you want more information, I wrote about this scenario here: http://reedcopsey.com/2009/04/19/idisposable-part-3-encapsulating-an-idisposable-class/ – Reed Copsey Aug 30 '11 at 19:40

7 Answers7

9

Yes - if your class "contains" an IDisposable, that class should almost certainly implement IDisposable too.

"Managed" resources are basically memory. "Unmanaged" resources can be file handles, network connections, handles to graphics objects etc. In most cases types which have direct access to native handles have finalizers, so the resource will be released at some point, but it's still better to release it explicitly - in some cases (such as with HttpWebResponse) there can be a limited number of such resources available (connections in a connection pool to a single host in this case) and you can end up timing out waiting for a "dead" resource to be freed.

Where possible, it's nicer not to have such class members in the first place - have them as method parameters of local variables etc, so you can use them and then close them without tying the lifetime of the resource to the lifetime of your object. However, in some cases that's not appropriate - in which case you should implement IDisposable.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think it's important to note that "unmanaged" resources can be practically anything, and anywhere; they may exist entirely within the managed world (e.g. a class which allocates items out of a pool held in a static array so as to minimize GC pressure), or may exist somewhere other than the current machine (e.g. a remote machine may have granted exclusive access to a database record). Basically anything the GC won't know how to clean up. – supercat Aug 31 '11 at 00:05
9

Three simple rules.

A managed resource is anything implementing IDisposable. An unmanaged resource is something like a HANDLE that you got via p/Invoke. A class like SafeHandle (or one derived from SafeHandle) owns an unmanaged resource, but it is considered a managed resource itself. So any class that owns unmanaged resource is itself a managed resource.

Since you have a class owning managed resources, follow Rule 2: implement IDisposable (but not a finalizer).

IDisposable allows for earlier cleanup. If you don't call it, the resources will be cleaned up anyway (they won't hang around until process exit); they'll just be cleaned up later, and you don't have a choice about when they get cleaned up.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thank for the clear answer. This is what I was looking for. I saw so many blogs with conflicting information. The page you linked is simple and clear. – Trevor Balcom Aug 30 '11 at 20:21
  • Does your answer still apply if I tell you the class inherits from Control and is not sealed? Control already has a Dispose method. – Trevor Balcom Aug 30 '11 at 20:32
  • Inheriting from managed resources is more complex. Microsoft has their [own rather confusing pattern](http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx) for this. What I recommend is: override `Dispose(bool)` (calling `base.Dispose(bool)`) and dispose of your managed resources only if `true` is passed into that method. Otherwise, do nothing. In this situation, your class should not have any unmanaged resources. – Stephen Cleary Aug 30 '11 at 21:15
  • 2
    Some classes that implement IDisposable will be properly cleaned up if abandoned. Not all will, however. If an interator calls "yield return" within a lock, and the IEnumerator returned by that iterator is abandoned without being Dispose'd, the lock will never get released. – supercat Aug 31 '11 at 00:08
  • 1
    Personally I wouldn't call "anything implementing IDisposable" a managed resource - in particular, I *would* call memory a managed resource, and that's managed by the garbage collector - whereas I would call anything that benefits from explicit cleanup unmanaged, either directly (a "native" handle) or indirectly (via another CLR type). – Jon Skeet Aug 31 '11 at 01:07
  • 1
    @Jon: I see your point about terminology, but I wish to distinguish between raw native resources and CLR-wrapped resources. [Microsoft sometimes uses](http://msdn.microsoft.com/en-us/ms244737.aspx) the terms "managed resource" for any `IDisposable`, and [other times uses](http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx) the (IMO awkward) "managed objects that use native resources" to refer to disposable types. – Stephen Cleary Aug 31 '11 at 13:09
  • 1
    @Stephen: I think it's reasonable to distinguish between those two, but I generally talk about types which have direct access to unmanaged resources and those which have indirect access. – Jon Skeet Aug 31 '11 at 13:11
  • Stephen, I followed your suggestion of overriding Dispose(bool). I'm always calling base.Dispose(bool). I call Dispose on the timer if the bool is set to true. I've run the program in the debugger with a breakpoint set at the top of this method. The breakpoint was hit after Application.Exit was called, but the bool passed in was false, so the method didn't execute the timer.Dispose. That's the opposite of what I expected to happen. – Trevor Balcom Aug 31 '11 at 14:16
  • 1
    @Trevor: This is expected (and correct) if you never call `Dispose` on your derived class. In this case, your derived `Dispose` is being called from the finalizer. – Stephen Cleary Aug 31 '11 at 15:33
2

Very comprehensive IDisposable guidelines are here.

Do transitively dispose of any disposable fields defined in your type from your Dispose method.

You should call Dispose() on any fields whose lifecycle your object controls. For example, consider a case where your object owns a private TextReader field. In your type's Dispose, you should call the TextReader object's Dispose, which will in turn dispose of its disposable fields (Stream and Encoding, for example), and so on. If implemented inside a Dispose(bool disposing) method, this should only occur if the disposing parameter is true—touching other managed objects is not allowed during finalization. Additionally, if your object doesn’t own a given disposable object, it should not attempt to dispose of it, as other code could still rely on it being active. Both of these could lead to subtle-to-detect bugs.

Do implement the dispose pattern when your type is unsealed and contains resources that explicitly need to be or can be freed, for example raw handles, or other unmanaged resources.

This pattern provides a standardized means for developers to deterministically destroy or free resources owned by an object. It also aids subclasses to correctly release base class resources.

'Unmanaged resource' usually refers to cases where your code refernces native handles directly (file handles, connections, sockets etc). In this case you would also have to implement finalizer or use SafeHandle. Most of the time you reference native handles indirectly, through .NET classes like TextReader. In this case you can simply use 'using' or, if you are writing library, implement IDisposable transitively.

Community
  • 1
  • 1
Dmitry
  • 17,078
  • 2
  • 44
  • 70
1

If your class has member variables that implement IDisposable, then your class should implement it as well. You clean up what you own.

Marlon
  • 19,924
  • 12
  • 70
  • 101
1

You have some good information and some misinformation in your understanding.

The long and short of it is that you need to Dispose() anything that implements IDisposable.

Being as these are private member variables of your class, and if these are supposed to be available for the lifetime of instances of that class, your class should also implement IDisposable and Dispose() of those types in its own Dispose() method.

If those private member variables have a limited lifetime (i.e. only within one method), just wrap them in a using block.

Jesse C. Slicer
  • 19,901
  • 3
  • 68
  • 87
0

1) You can use a Memory Profiler Tool, there are plenty around the web, the best i know being Reg Gate's ANTS Profiler.

2) My rule of thumb is that events must always be unsubscribed, and disposable objects (Streams etc) will be disposed automatically if they're member variables and the object holding them gets destroyed. If you create a local disposable object in a method for example, you must dispose it, or just put it in a using statement and forget about it ;)

Louis Kottmann
  • 16,268
  • 4
  • 64
  • 88
0

I think it's most helpful to describe a managed resource is a class-type object that implements IDisposable and requires cleanup, but can perform such cleanup (typically using Finalize) if it's abandoned without being properly Dispose'd. An unmanaged resource generally refers to an entity which requires cleanup that simply won't happen if it's abandoned without being Dispose'd first. It's important to note that the while the term "managed resource" refers essentially exclusively to class-type objects (which generally override Finalize, but which in some cases may be the targets of WeakReference objects), unmanaged resources may be not only be anything, they may also be anywhere, including on another computer.

I would suggest that instead of using the term "resource" it's more helpful to think in terms of "responsibilities". Opening a file or socket connection creates a responsibility to close it. Acquiring a lock creates a responsibility to release it. Sending a remote system a "grant me exclusive access to this record" message creates a responsibility to send it an "I'm done with this record" message. If an object's cleanup responsibilites can get carried out even if it's abandoned, it's a "managed resource". Otherwise, it's an "unmanaged resource".

Merely categorizing things as "managed resources" or "unmanaged resources" is not quite sufficient for deciding how they should be cleaned up. Some unmanaged responsibilities can be conveniently handled by a class-type wrapper which can perform any necessary cleanup in case of improper abandonment. Such wrappers should generally contain a minimal amount of information necessary to perform the cleanup responsibility. Other responsibilities cannot very well be handled automatically. It's often better to ensure Dispose is called than try to handle everything that can happen if it isn't.

supercat
  • 77,689
  • 9
  • 166
  • 211