Implementing a Dispose Method
The pattern for disposing an object, referred to as a dispose pattern, imposes order on the lifetime of an object. The dispose pattern is used only for objects that access unmanaged resources. This is because the garbage collector is very efficient at reclaiming unused managed objects.
A type's Disposemethod should release all the resources that it owns. It should also release all resources owned by its base types by calling its parent type's Dispose method. The parent type's Dispose method should release all resources that it owns and in turn call its parent type's Dispose method, propagating this pattern through the hierarchy of base types. To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.
There is no performance benefit in implementing the Dispose method on types that use only managed resources (such as arrays) because they are automatically reclaimed by the garbage collector. Use the Dispose method primarily on managed objects that use native resources and on COM objects that are exposed to the .NET Framework. Managed objects that use native resources (such as the FileStream class) implement the IDisposable interface.
Important |
|---|
C++ programmers should not use this topic. Instead, see Destructors and Finalizers in Visual C++. In the .NET Framework version 2.0, the C++ compiler provides support for implementing deterministic disposal of resources and does not allow direct implementation of the Dispose method. |
A Dispose method should call the SuppressFinalize method for the object it is disposing. If the object is currently on the finalization queue, SuppressFinalize prevents its Finalize method from being called. Remember that executing a Finalize method is costly to performance. If your Dispose method has already done the work to clean up the object, then it is not necessary for the garbage collector to call the object's Finalize method.
The code example provided for the GC.KeepAlive method shows how aggressive garbage collection can cause a finalizer to run while a member of the reclaimed object is still executing. It is a good idea to call the KeepAlive method at the end of a lengthy Dispose method.
Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. Therefore, we recommend that you construct SafeHandle objects instead of implementing the dispose pattern.
The SafeHandle class simplifies object lifetime issues by assigning and releasing handles without interruption. It contains a critical finalizer that is guaranteed to run while an application domain is unloading. For more information about the advantages of using a safe handle, see Safe Handles and Critical Finalization.
The SafeHandle class in the System.Runtime.InteropServices namespace is an abstract wrapper class for operating system handles. Deriving from this class is difficult. Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:
-
Files and pipes.
-
Memory views.
-
Cryptography constructs.
-
Registry keys.
-
Wait handles.
The following code example shows the recommended design pattern for implementing a Dispose method for classes that encapsulate unmanaged resources.
Resource classes are typically derived from complex native classes or APIs and must be customized accordingly. Use this code pattern as a starting point for creating a resource class and provide the necessary customization based on the resources you are encapsulating.
using System; using System.IO; class Program { static void Main() { try { // Initialize a Stream resource to pass // to the DisposableResource class. Console.Write("Enter filename and its path: "); string fileSpec = Console.ReadLine(); FileStream fs = File.OpenRead(fileSpec); DisposableResource TestObj = new DisposableResource(fs); // Use the resource. TestObj.DoSomethingWithResource(); // Dispose the resource. TestObj.Dispose(); } catch (FileNotFoundException e) { Console.WriteLine(e.Message); } } } // This class shows how to use a disposable resource. // The resource is first initialized and passed to // the constructor, but it could also be // initialized in the constructor. // The lifetime of the resource does not // exceed the lifetime of this instance. // This type does not need a finalizer because it does not // directly create a native resource like a file handle // or memory in the unmanaged heap. public class DisposableResource : IDisposable { private Stream _resource; private bool _disposed; // The stream passed to the constructor // must be readable and not null. public DisposableResource(Stream stream) { if (stream == null) throw new ArgumentNullException("Stream in null."); if (!stream.CanRead) throw new ArgumentException("Stream must be readable."); _resource = stream; _disposed = false; } // Demonstrates using the resource. // It must not be already disposed. public void DoSomethingWithResource() { if (_disposed) throw new ObjectDisposedException("Resource was disposed."); // Show the number of bytes. int numBytes = (int) _resource.Length; Console.WriteLine("Number of bytes: {0}", numBytes.ToString()); } public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass // of this type implements a finalizer. GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // If you need thread safety, use a lock around these // operations, as well as in your methods that use the resource. if (!_disposed) { if (disposing) { if (_resource != null) _resource.Dispose(); Console.WriteLine("Object disposed."); } // Indicate that the instance has been disposed. _resource = null; _disposed = true; } } }
if(_resource != null) _resource.Dispose();
If two users call the dispose method on different threads - one thread may dispose the internal resource and set "_resource = null;" *after* the second thread checked for its nullness.
A lock should encapsulate the entire section. :(
- 3/14/2011
- Ariel Raunstien
- 4/6/2012
- Thomas Lee
if (_resource != null)
_resource.Dispose();
Between the test that _resource is not null and the call to _resource.Dispose(), another thread can call Dispose(true) and execute this statement:
_resource = null;
which will cause the other thread to throw a NullReferenceException.
- 11/17/2011
- Fran Litterio
- 4/6/2012
- Thomas Lee
GC.SuppressFinalize(this);
? It would be better to use
if (_resource != null)
{
_resource.Dispose();
GC.SuppressFinalize(_resource);
}
as this should be cleared by GS, but it's _resource that should not.
- 3/28/2012
- amarok888
This sample demonstrates the same thing as this other sample: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx
The other sample is better because it demonstrates the finalizer implementation as destructor which the present sample lacks. That finalizer makes it much easier to understand why there is an overloaded Dispose method taking a bool parameter. IMHO the two samples should be reconciled.
- 5/4/2011
- David Burg
- Dispose(true) is called by user-code.
- Dispose(false) should be called from the finalizer.
- 11/8/2010
- spacedumpling
- 1/8/2011
- waldred
I think it is always good practice to make the private member _disposed as protected readonly property.With all due respect, I would strongly recommend against this. The internal _disposed field represents state internal to the specific type. Remember that the Dispose(bool) is not guaranteed to succeed at any level in the inheritance chain. This means the _disposed field could be set to "true" and an exception occurs later as we bubble up the chain. If this happens, the _disposed field is incorrect which could potentially lead to memory leaks and such later.
Setting this property is the responsibility of the base class.
Therefore the inheritors' disposal can check this state.
The _disposed field should only represent state of the immediate type in which it is implemented. Other types in the hierarchy may have their own _disposed fields if necessary, all as private members.
(There are ways to mitigate the potential issue mentioned above such as always performing your own dispose cleanup before calling into the base Dispose method but you can only guarantee pattern this if you have 100% control over the hierarchy over time [and no bugs along the way]. If you're writing framework code used by other developers, you won't have this guarantee.)
Important