Export (0) Print
Expand All

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, such as file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects.

The dispose pattern has two variations:

To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

Important note Important

If you're a C++ programmer, do not implement the Dispose method. Instead, follow the instructions in the "Destructors and finalizers" section of How to: Define and Consume Classes and Structs (C++/CLI). Starting with the .NET Framework 2.0, the C++ compiler supports the deterministic disposal of resources and does not allow direct implementation of the Dispose 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.

The IDisposable interface requires the implementation of a single parameterless method, Dispose. However, the dispose pattern requires two Dispose methods to be implemented:

  • A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • A protected virtual (Overridable in Visual Basic) Dispose method whose signature is:

Because the public, non-virtual (NonInheritable in Visual Basic), parameterless Dispose method is called by a consumer of the type, its purpose is to free unmanaged resources and to indicate that the finalizer, if one is present, doesn't have to run. Because of this, it has a standard implementation:

Public Sub Dispose() _
           Implements IDisposable.Dispose
   ' Dispose of unmanaged resources.
   Dispose(True)
   ' Suppress finalization.
   GC.SuppressFinalize(Me)
End Sub

The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Note that the actual work of releasing unmanaged resources is performed by the second overload of the Dispose method.

In the second overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).

The body of the method consists of two blocks of code:

  • A block that frees unmanaged resources. This block executes regardless of the value of the disposing parameter.

  • A conditional block that frees managed resources. This block executes if the value of disposing is true. The managed resources that it frees can include:

    Managed objects that implement T:System.IDisposable.

    The conditional block can be used to call their Dispose implementation. If you have used a safe handle to wrap your unmanaged resource, you should call the SafeHandle.Dispose(Boolean) implementation here.

    Managed objects that consume large amounts of memory or consume scarce resources.

    Freeing these objects explicitly in the Dispose method releases them faster than if they were reclaimed non-deterministically by the garbage collector.

If the method call comes from a finalizer (that is, if disposing is false), only the code that frees unmanaged resources executes. Because the order in which the garbage collector destroys managed objects during finalization is not defined, calling this Dispose overload with a value of false prevents the finalizer from trying to release managed resources that may have already been reclaimed.

If you implement the dispose pattern for a base class, you must provide the following:

Important note Important

You should implement this pattern for all base classes that implement IDisposable and are not sealed (NotInheritable in Visual Basic).

  • A Dispose implementation that calls the Dispose(Boolean) method.

  • A Dispose(Boolean) method that performs the actual work of releasing resources.

  • Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. The SafeHandle class provides a finalizer that frees you from having to code one.

Here's the general pattern for implementing the dispose pattern for a base class that uses a safe handle:

Class BaseClass : Implements IDisposable
   ' Flag: Has Dispose already been called? 
   Dim disposed As Boolean = False 

   ' Public implementation of Dispose pattern callable by consumers. 
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)           
   End Sub 

   ' Protected implementation of Dispose pattern. 
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return 

      If disposing Then 
         ' Free any other managed objects here. 
         
      End If 

      ' Free any unmanaged objects here. 
      '
      disposed = True 
   End Sub 
End Class

Here's the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize:

Class BaseClass : Implements IDisposable
   ' Flag: Has Dispose already been called? 
   Dim disposed As Boolean = False 

   ' Public implementation of Dispose pattern callable by consumers. 
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)           
   End Sub 

   ' Protected implementation of Dispose pattern. 
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return 

      If disposing Then 
         ' Free any other managed objects here. 
         
      End If 

      ' Free any unmanaged objects here. 
      '
      disposed = True 
   End Sub 

   Protected Overrides Sub Finalize()
      Dispose(False)      
   End Sub 
End Class

A class derived from a class that implements the IDisposable interface shouldn't implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. Instead, to implement the dispose pattern for a derived class, you provide the following:

  • A protected Dispose(Boolean) method that overrides the base class method and performs the actual work of releasing the resources of the derived class. This method should also call the Dispose(Boolean) method of the base class and pass it a value of true for the disposing argument.

  • Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. The SafeHandle class provides a finalizer that frees you from having to code one. If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false.

Here's the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:

Class DerivedClass : Inherits BaseClass 
   ' Flag: Has Dispose already been called? 
   Dim disposed As Boolean = False 

   ' Protected implementation of Dispose pattern. 
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return 

      If disposing Then 
         ' Free any other managed objects here. 
         
      End If 

      ' Free any unmanaged objects here. 
      '
      disposed = True 

      ' Call base class implementation. 
      MyBase.Dispose(disposing)
   End Sub 
End Class

Here's the general pattern for implementing the dispose pattern for a derived class that overrides Object.Finalize:

Class DerivedClass : Inherits BaseClass
   ' Flag: Has Dispose already been called? 
   Dim disposed As Boolean = False 

   ' Protected implementation of Dispose pattern. 
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return 

      If disposing Then 
         ' Free any other managed objects here. 
         
      End If 

      ' Free any unmanaged objects here. 
      '
      disposed = True 

      ' Call the base class implementation. 
      MyBase.Dispose(disposing)
   End Sub 

   Protected Overrides Sub Finalize()
      Dispose(False)
   End Sub  
End Class

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 System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

Classes derived from the System.Runtime.InteropServices.SafeHandle class simplify object lifetime issues by assigning and releasing handles without interruption. They contain 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 following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. It defines a DisposableResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. The DisposableResource method also includes a single property, Size, that returns the total number of bytes in the file stream.

Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource : Implements IDisposable
   ' Define constants. 
   Protected Const GENERIC_READ As UInteger = &H80000000ui
   Protected Const FILE_SHARE_READ As UInteger = &H0000000i
   Protected Const OPEN_EXISTING As UInteger = 3
   Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
   Protected INVALID_HANDLE_VALUE As New IntPtr(-1)
   Private Const INVALID_FILE_SIZE As Integer = &HFFFFFFFF

   ' Define Windows APIs. 
   Protected Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (
                                         lpFileName As String, dwDesiredAccess As UInt32, 
                                         dwShareMode As UInt32, lpSecurityAttributes As IntPtr, 
                                         dwCreationDisposition As UInt32, dwFlagsAndAttributes As UInt32, 
                                         hTemplateFile As IntPtr) As IntPtr
   Private Declare Function GetFileSize Lib "kernel32" (hFile As SafeFileHandle, 
                                                        ByRef lpFileSizeHigh As Integer) As Integer 

   ' Define locals. 
   Private disposed As Boolean = False 
   Private safeHandle As SafeFileHandle 
   Private bufferSize As Long  
   Private upperWord As Integer 

   Public Sub New(filename As String)
      If filename Is Nothing Then 
         Throw New ArgumentNullException("The filename cannot be null.")
      Else If filename = "" 
         Throw New ArgumentException("The filename cannot be an empty string.")
      End If 

      Dim handle As IntPtr = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                        IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                        IntPtr.Zero)
      If handle <> INVALID_HANDLE_VALUE Then
         safeHandle = New SafeFileHandle(handle, True)
      Else 
         Throw New FileNotFoundException(String.Format("Cannot open '{0}'", filename))
      End If 

      ' Get file size.
      bufferSize = GetFileSize(safeHandle, upperWord) 
      If bufferSize = INVALID_FILE_SIZE Then
         bufferSize = -1
      Else If upperWord > 0 Then 
         bufferSize = (CLng(upperWord) << 32) + bufferSize
      End If      
   End Sub 

   Public ReadOnly Property Size As Long 
      Get 
         Return bufferSize
      End Get 
   End Property 

   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub            

   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Exit Sub 

      ' Dispose of managed resources here. 
      If disposing Then
         safeHandle.Dispose()
      End If 

      ' Dispose of any unmanaged resources not wrapped in safe handles.

      disposed = True 
   End Sub   
End Class

The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. The class adds an additional method, WriteFileInfo, and uses a SafeFileHandle object to wrap the handle of the writable file.

Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource2 : Inherits DisposableStreamResource
   ' Define additional constants. 
   Protected Const GENERIC_WRITE As Integer = &H40000000 
   Protected Const OPEN_ALWAYS As Integer = 4

   ' Define additional APIs. 
   Protected Declare Function WriteFile Lib "kernel32.dll" (
                              safeHandle As SafeFileHandle, lpBuffer As String, 
                              nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer,
                              lpOverlapped As Object) As Boolean 

   ' Define locals. 
   Private disposed As Boolean = False 
   Private filename As String 
   Private created As Boolean = False 
   Private safeHandle As SafeFileHandle

   Public Sub New(filename As String)
      MyBase.New(filename)
      Me.filename = filename
   End Sub 

   Public Sub WriteFileInfo() 
      If Not created Then 
         Dim hFile As IntPtr = CreateFile(".\FileInfo.txt", GENERIC_WRITE, 0, 
                                          IntPtr.Zero, OPEN_ALWAYS, 
                                          FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
         If hFile <> INVALID_HANDLE_VALUE Then
            safeHandle = New SafeFileHandle(hFile, True)
         Else 
            Throw New IOException("Unable to create output file.")
         End If
         created = True 
      End If 
      Dim output As String = String.Format("{0}: {1:N0} bytes {2}", filename, Size, 
                                           vbCrLf)
      WriteFile(safeHandle, output, output.Length, 0&, Nothing)                                     
   End Sub 

   Protected Overloads Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Exit Sub 

      ' Release any managed resources here. 
      If disposing Then
         safeHandle.Dispose()
      End If 

      ' Release any unmanaged resources not wrapped by safe handles here. 

      ' Call the base class implementation. 
      MyBase.Dispose(True)
   End Sub 
End Class
Show:
© 2014 Microsoft