IDisposable Part 1 – Releasing Unmanaged Resources
IDisposable, what is it, why is it needed? In this part of our series on IDisposable, we’re going to focus on the initial, and primary use case of IDisposable – wrapping unmanaged resources…
From MSDN on IDisposable: “The primary use of this interface is to release unmanaged resources.â€
One of the main reasons IDisposable exists is to deterministically release any unmanaged resource that a class contains. This can be a native, Win32 file handle, socket handle, or any other native, unmanaged resource. One of the joys of working with C# and .NET is the built in garbage collector – this frees us as developers from some of the most problematic issues in programming. The garbage collector automatically releases allocated but unused memory in managed objects for us – eliminating most memory leaks entirely. However, there are two issues with the garbage collector that come into play with unmanaged, native resources.
- The garbage collector runs when it wants to run, not necessarily when you are done with your object. There is no mechanism available to predict when the garbage collector will clean up your object, other than forcing a full garbage collection using GC.Collect, which is not recommended.
- The garbage collector will clean up all of your standard C# classes, since it understands the .NET type system, but it has no knowledge of unmanaged, native resources. For example, it does not know that an IntPtr refers to a native file handle, and must be closed with a specific API in order to release the handle.
When we’re using native resources, there are two issues we need to address using IDisposable. The first relates to timing – if we’re using a native resource, and we want to stop using it, we should have a way to instantly stop using that resource. The second relates to safety – if we’re using a native resource, and are now done with it, we need a way to make sure that resource is successfully cleaned. IDisposable can help with both of these issues.
To help illustrate where this may be required, and how it might be used, let’s take an example case. Say we have an old, legacy C++ licensing library, with a C API. In our case, our C API provides a way to construct a native class, call some methods on the class, and also cleanup our class, as follows:
void *new_license_generator(); int license_valid(void *handle, char *username, int license_type); void delete_license_generator(void *handle);
In this case, the three function calls do the following.
- “new_license_generator†creates a new “License Generator†class defined in C++, and returns a LicenseGenerator* cast to a void*.
- “license_valid†takes a pointer to a license generator, a username as a string, and a license type as an integer, and returns a 0 if the license is bad, and non-zero if it’s good.
- “delete_license_generator†takes a pointer to a “LicenseGenerator†class, and deletes it using the same memory allocation library that constructed it.
Now, if we want to access this from C#, and wrap it in a managed class, we need to implement IDisposable. The first option we have in making a wrapper class for this is whether or not to allow subclassing. The choice of whether to include subclassing has an effect on us – if we allow subclassing, we implement IDisposable slightly differently. In general, I think a good rule of thumb to follow is to make any class that is going to directly wrap a native API sealed. However, this isn’t a fixed requirement.
In our case, we’re going to have to focus on a few rules with IDisposable. First off, we need to implement the interface. This means adding:
public void Dispose();
That’s it. However, this isn’t enough to implement it correctly – here are some rules we will need to follow:
- Dispose() must be callable multiple times, with no consequences.
- The type should also implement a finalizer, in order to protect the class from not freeing the resources if the caller does not call dispose properly.
- Dispose() should call GC.SuppressFinalize to prevent the finalizer from running once Dispose() has been called
- Any method called after the call to Dispose() should throw an ObjectDisposedException
We also want to use the standard pattern for implementing IDisposable. So here is an example of how we would go about this. First, we create our NativeMethods in a static class via pInvoke.
internal static class NativeMethods { [DllImport("myLibrary.dll", EntryPoint="new_license_generatorâ€, CharSet = CharSet.Ansi)] internal static extern IntPtr CreateLicenseGenerator(); [DllImport("myLibrary.dll", EntryPoint="license_validâ€, CharSet = CharSet.Ansi)] internal static extern int LicenseValid(IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string userName, int licenseType); [DllImport("myLibrary.dll", EntryPoint="delete_license_generatorâ€, CharSet=CharSet.Ansi)] internal static extern void DeleteLicenseGenerator(IntPtr handle); }
Next, we make our public class to wrap the native methods. In our case, we’re going to allow subclassing, so this class will not be sealed. In the next installment, we’ll subclass our LicenseGenerator.
/// <summary> /// Our License Generator class /// </summary> public class LicenseGenerator : IDisposable { /// <summary> /// Have we been disposed /// </summary> private bool disposed; /// <summary> /// Our native handle to our unmanaged resource /// </summary> private IntPtr nativeHandle; /// <summary> /// The license type we're checking /// </summary> private LicenseType licenseType; /// <summary> /// The type of licenses we support /// </summary> public enum LicenseType { LicenseA = 1, LicenseB = 2, LicenseC = 4 } /// <summary> /// Initializes a new instance of the <see cref="LicenseGenerator"/> class. /// </summary> public LicenseGenerator(LicenseType licenseType) { // No error checking on the return here for simplicity this.nativeHandle = NativeMethods.CreateLicenseGenerator(); this.licenseType = licenseType; } /// <summary> /// Releases unmanaged resources and performs other cleanup operations before the /// <see cref="LicenseGenerator"/> is reclaimed by garbage collection. /// </summary> /// <remarks>We must implement a finalizer to guarantee that our native handle is cleaned up</remarks> ~LicenseGenerator() { // Our finalizer should call our Dispose(bool) method with false this.Dispose(false); } /// <summary> /// Checks to see if a license is valid /// </summary> /// <param name="userName">Name of the user.</param> /// <returns>True if the license is valid, otherwise false.</returns> public virtual bool LicenseValid(string userName) { // We need a check on each method to make sure we're not disposed already if (this.disposed) { throw new ObjectDisposedException("LicenseGenerator"); } // Call into our native API safely at this point... int result = NativeMethods.LicenseValid(this.nativeHandle, userName, (int) this.licenseType); if (result == 0) { return false; } return true; } /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <remarks> /// If the main class was marked as sealed, we could just make this a private void Dispose(bool). Alternatively, we could (in this case) put /// all of our logic directly in Dispose(). I prefer sticking to the same pattern for sealed classes, however. /// </remarks> protected virtual void Dispose(bool disposing) { // Use our disposed flag to allow us to call this method multiple times safely. // This is a requirement when implementing IDisposable if (!this.disposed) { if (disposing) { // If we have any managed, IDisposable resources, Dispose of them here. // In this case, we don't, so this was unneeded. // Later, we will subclass this class, and use this section. } // Always dispose of undisposed unmanaged resources in Dispose(bool) NativeMethods.DeleteLicenseGenerator(this.nativeHandle); this.nativeHandle = IntPtr.Zero; } // Mark us as disposed, to prevent multiple calls to dispose from having an effect, // and to allow us to handle ObjectDisposedException this.disposed = true; } /// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> public void Dispose() { // We start by calling Dispose(bool) with true this.Dispose(true); // Now suppress finalization for this object, since we've already handled our resource cleanup tasks GC.SuppressFinalize(this); } }
Note the remarks throughout the class. We are handling IDisposable by adding three methods:
- ~LicenseGenerator() – Our finalizer. Any class with native resources should contain a finalizer. This way, if a consumer of our class forgets to call Dispose(), or does not use the class instance within a using statement, our native resource will still get cleaned up properly.
- public void Dispose() – The IDisposable interface itself.
- protected virtual void Dispose(bool disposing) – The routine that does the actual cleanup (via Platform Invoke). Note that it has a separate source path if your class owns any managed, but IDisposable objects.
We also check in LicenseValid to verify that the class has not already been disposed. This is a requirement of implementing IDisposable correctly. Every method in the class should check to see if it has already been disposed.
IDisposable Part 1 – Releasing Unmanaged Resources
: web page is pretty helpful to me for clearing my basics about when a memory leak can happen..with unmanaged resources
Why should we call the virtual dispose method from the finalizer with the false parameter ?
Mohinder –
Calling the Dispose method from the finalizer is what guarantees that the unmanaged resource will get cleaned up, even if the user fails to call Dispose() on the object.
Thanks a lotttttttttttttttttttttttttttttttt…!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
I spent around 4-5 hours to look for such an article where I can see that working.
Almost all the articles say that call “dispose” method and unmanaged resources are frees as if dispose does something like deallocating the unmanaged memory somehow or send some instructions to OS to claim unmanaged resource but in reality we ourself write that code explicitly to deallocate unmanaged resources
NativeMethods.DeleteLicenseGenerator(this.nativeHandle);
This line just cleared everything for me. Thanks for writing this article.
Found another Jon Skeet 🙂