patterncsharpCritical
Proper use of the IDisposable interface
Viewed 0 times
usetheproperidisposableinterface
Problem
I know from reading Microsoft documentation that the "primary" use of the
To me, "unmanaged" means things like database connections, sockets, window handles, etc. But, I've seen code where the
For example:
My question is, does this make the garbage collector free memory used by
Edit: So far people have posted some good examples of using
IDisposable interface is to clean up unmanaged resources.To me, "unmanaged" means things like database connections, sockets, window handles, etc. But, I've seen code where the
Dispose() method is implemented to free managed resources, which seems redundant to me, since the garbage collector should take care of that for you.For example:
public class MyCollection : IDisposable
{
private List _theList = new List();
private Dictionary _theDict = new Dictionary();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
}
My question is, does this make the garbage collector free memory used by
MyCollection any faster than it normally would?Edit: So far people have posted some good examples of using
IDisposable to clean up unmanaged resources such as database connections and bitmaps. But suppose that _theList in the above code contained a million strings, and you wanted to free that memory now, rather than waiting for the garbage collector. Would the above code accomplish that?Solution
The point of Dispose is to free unmanaged resources. It needs to be done at some point, otherwise they will never be cleaned up. The garbage collector doesn't know how to call
Note: What is an unmanaged resource? If you found it in the Microsoft .NET Framework: it's managed. If you went poking around MSDN yourself, it's unmanaged. Anything you've used P/Invoke calls to get outside of the nice comfy world of everything available to you in the .NET Framework is unmanaged – and you're now responsible for cleaning it up.
The object that you've created needs to expose some method, that the outside world can call, in order to clean up unmanaged resources. The method can be named whatever you like:
or
But instead there is a standardized name for this method:
There was even an interface created,
So you make your object expose the
And you're done.
Except you can do better
What if your object has allocated a 250MB System.Drawing.Bitmap (i.e. the .NET managed Bitmap class) as some sort of frame buffer? Sure, this is a managed .NET object, and the garbage collector will free it. But do you really want to leave 250MB of memory just sitting there – waiting for the garbage collector to eventually come along and free it? What if there's an open database connection? Surely we don't want that connection sitting open, waiting for the GC to finalize the object.
If the user has called
So now we will:
So let's update our
And all is good.
Except you can do better!
What if the person forgot to call
Note: They won't leak managed resources, because eventually the garbage collector is going to run, on a background thread, and free the memory associated with any unused objects. This will include your object, and any managed objects you use (e.g. the
If the person forgot to call
Note: The garbage collector will eventually free all managed objects.
When it does, it calls the
method on the object. The GC doesn't know, or
care, about your Dispose method.
That was just a name we chose for
a method we call when we want to get
rid of unmanaged stuff.
The destruction of our object by the Garbage collector is the perfect time to free those pesky unmanaged resources. We do this by overriding the
Note: In C#, you don't explicitly override the
You write a method that looks like a C++ destructor, and the
compiler takes that to be your implementation of the
But there's a bug in that code. You see, the garbage collector runs on a background thread; you don't know the order in which two objects are destroyed. It is entirely possible that in your
So what you need is a way for `Finalize(
DeleteHandle() on a variable of type IntPtr, it doesn't know whether or not it needs to call DeleteHandle().Note: What is an unmanaged resource? If you found it in the Microsoft .NET Framework: it's managed. If you went poking around MSDN yourself, it's unmanaged. Anything you've used P/Invoke calls to get outside of the nice comfy world of everything available to you in the .NET Framework is unmanaged – and you're now responsible for cleaning it up.
The object that you've created needs to expose some method, that the outside world can call, in order to clean up unmanaged resources. The method can be named whatever you like:
public void Cleanup()or
public void Shutdown()But instead there is a standardized name for this method:
public void Dispose()There was even an interface created,
IDisposable, that has just that one method:public interface IDisposable
{
void Dispose();
}So you make your object expose the
IDisposable interface, and that way you promise that you've written that single method to clean up your unmanaged resources:public void Dispose()
{
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}And you're done.
Except you can do better
What if your object has allocated a 250MB System.Drawing.Bitmap (i.e. the .NET managed Bitmap class) as some sort of frame buffer? Sure, this is a managed .NET object, and the garbage collector will free it. But do you really want to leave 250MB of memory just sitting there – waiting for the garbage collector to eventually come along and free it? What if there's an open database connection? Surely we don't want that connection sitting open, waiting for the GC to finalize the object.
If the user has called
Dispose() (meaning they no longer plan to use the object) why not get rid of those wasteful bitmaps and database connections?So now we will:
- get rid of unmanaged resources (because we have to), and
- get rid of managed resources (because we want to be helpful)
So let's update our
Dispose() method to get rid of those managed objects:public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}And all is good.
Except you can do better!
What if the person forgot to call
Dispose() on your object? Then they would leak some unmanaged resources!Note: They won't leak managed resources, because eventually the garbage collector is going to run, on a background thread, and free the memory associated with any unused objects. This will include your object, and any managed objects you use (e.g. the
Bitmap and the DbConnection).If the person forgot to call
Dispose(), we can still save their bacon! We still have a way to call it for them: when the garbage collector finally gets around to freeing (i.e. finalizing) our object.Note: The garbage collector will eventually free all managed objects.
When it does, it calls the
Finalizemethod on the object. The GC doesn't know, or
care, about your Dispose method.
That was just a name we chose for
a method we call when we want to get
rid of unmanaged stuff.
The destruction of our object by the Garbage collector is the perfect time to free those pesky unmanaged resources. We do this by overriding the
Finalize() method.Note: In C#, you don't explicitly override the
Finalize() method.You write a method that looks like a C++ destructor, and the
compiler takes that to be your implementation of the
Finalize() method:~MyObject()
{
//we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
Dispose(); //<--Warning: subtle bug! Keep reading!
}But there's a bug in that code. You see, the garbage collector runs on a background thread; you don't know the order in which two objects are destroyed. It is entirely possible that in your
Dispose() code, the managed object you're trying to get rid of (because you wanted to be helpful) is no longer there:public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
this.frameBufferImage = null;
}
}So what you need is a way for `Finalize(
Code Snippets
public void Cleanup()public void Shutdown()public void Dispose()public interface IDisposable
{
void Dispose();
}public void Dispose()
{
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}Context
Stack Overflow Q#538060, score: 3014
Revisions (0)
No revisions yet.