Skip to main content
黑话筒

转载:实作 Dispose 方法

型别的 Dispose 方法应该释放它所拥有的所有资源。它也应该藉由呼叫其基底型别之父型别的 Dispose 方法,来释放其基底型别所拥有的所有资源。这个父型别的 Dispose 方法应该释放它所拥有的所有资源,并且接续呼叫其父型别的 Dispose 方法,依照这个模式传播到整个基底型别的阶层架构。若要确保资源永远都能适当地清除,Dispose 方法应该能够被重复呼叫多次而不会掷回例外情况 (Exception)。

Dispose 方法应该呼叫它正在处置之物件的 GC.SuppressFinalize 方法。如果物件目前正在最终处理伫列,GC.SuppressFinalize 会防止它的 Finalize 方法被呼叫。请记住,执行 Finalize 方法对效能会有很大影响。如果 Dispose 方法已经执行完清除物件的工作,那麽就不需要再让记忆体回收行程去呼叫该物件的 Finalize 方法。

下列程式码范例的目的是要说明一种可能的设计模式,这种模式可为封装 Unmanaged 资源的类别实作 Dispose 方法。您可能会发现这个模式用起来相当方便,因为它已实作於整个 .NET Framework。不过,这并不是 Dispose 方法唯一可能的实作。

资源类别通常是从复杂的原生类别或 API 衍生而来,因此必须依照情况加以自订。请将这个程式码模式当作建立资源类别的起始点,并且根据您正在封装的资源,提供必要的自订。您不能编译这个范例并直接在应用程式中使用它。

在这个范例中,基底类别 BaseResource 会实作一个可让类别使用者呼叫的公用 Dispose 方法。它会接着呼叫 virtual Dispose(bool disposing) 方法 (在 Visual Basic 中为 virtual Dispose(disposing As Boolean))。根据呼叫端的识别 (Identity) 来传递true 或 false。物件的适当清除程式码会在这个虚拟 Dispose 方法中执行。

Dispose(bool disposing) 可在两种不同的案例中执行。如果 disposing 等於 true,表示方法是由使用者的程式码直接或间接呼叫的,因此可以处置 Managed 和 Unmanaged 资源。如果 disposing 等於 false,表示方法是由 Runtime 从完成项内部呼叫的,因此只能处置 Unmanaged 资源。当物件正在执行它的最终处理程式码时,它不可以参考其他物件,因为完成项不会依照任何特定顺序执行。如果正在执行的完成项参考的另一个物件已经做过最终处理,那麽正在执行的完成项将会失败。

基底类别提供 Finalize 方法或解构函式,做为万一没有呼叫 Dispose 时的防护措施。Finalize 方法会呼叫使用参数传递 false 的 Dispose 方法。您不可以在 Finalize 方法内重新建立 Dispose 清除程式码。呼叫 Dispose(false) 对於程式码的可读性和可维护性是最佳方式。

MyResourceWrapper 类别说明了如何从使用 Dispose 实作资源管理的类别中衍生一个类别的方法。MyResourceWrapper 会覆写 virtual Dispose(bool disposing) 方法,并提供已建立 Managed 和 Unmanaged 资源的清除程式码。MyResourceWrapper 也会在它自己的基底类别 BaseResource 上呼叫 Dispose,以确保可以正常清除其所使用的基底。请注意,衍生类别 (Derived Class) MyResourceWrapper 并没有不含参数的 Finalize 方法或是 Dispose 方法,因为它会从基底类别 BaseResource 继承。

**注意 **此一范例中的 protected Dispose(bool disposing) 方法并不会强制使用执行绪安全,这是因为您无法同时从使用者执行绪和完成项执行绪呼叫该方法。此外,使用 BaseResource 的用户端应用程式还应该永远不让多个使用者执行绪同时呼叫 protected Dispose(bool disposing) 方法。应用程式或类别库 (Class Library) 应该设计成只能让一个执行绪拥有资源的存留期,并且要在不复需要资源时才呼叫 Dispose。视资源而定,未同步化的执行绪存取在处置资源时可能会造成安全性的风险。开发人员应该小心检视其程式码,并据以判断强制使用执行绪安全的最佳方式。

' Design pattern for the base class.
' By implementing IDisposable, you are announcing that instances
' of this type allocate scarce resources.
Public Class BaseResource
   Implements IDisposable
    ' Pointer to an external unmanaged resource.
   Private handle As IntPtr
    ' Other managed resource this class uses.
   Private Components As Component
    ' Track whether Dispose has been called.
   Private disposed As Boolean = False

    ' Constructor for the BaseResource Object.
   Public Sub New()
       ' Insert appropriate constructor code here.
   End Sub

    ' Implement IDisposable.
    ' Do not make this method Overridable.
    ' A derived class should not be able to override this method.
   Public Overloads Sub Dispose()Implements IDisposable.Dispose
      Dispose(true)
       ' Take yourself off of the finalization queue
       ' to prevent finalization code for this object
       ' from executing a second time.
      GC.SuppressFinalize(Me)
   End Sub

 ' Dispose(disposing As Boolean) executes in two distinct scenarios.
 ' If disposing is true, the method has been called directly
 ' or indirectly by a user 's code. Managed and unmanaged resources
 ' can be disposed.
 ' If disposing equals false, the method has been called by the runtime
 ' from inside the finalizer and you should not reference other
 ' objects. Only unmanaged resources can be disposed.
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
    ' Check to see if Dispose has already been called.
   If Not (Me.disposed) Then
       ' If disposing equals true, dispose all managed
       ' and unmanaged resources.
      If (disposing) Then
          ' Dispose managed resources.
         Components.Dispose()
      End If
       ' Release unmanaged resources. If disposing is false,
       ' only the following code is executed.
      CloseHandle(handle)
      handle = IntPtr.Zero
       ' Note that this is not thread safe.
       ' Another thread could start disposing the object
       ' after the managed resources are disposed,
       ' but before the disposed flag is set to true.
       ' If thread safety is necessary, it must be
       ' implemented by the client.
   End If
   Me.disposed = true
End Sub

    ' This Finalize method will run only if the
    ' Dispose method does not get called.
    ' By default, methods are NotOverridable.
    ' This prevents a derived class from overriding this method.
   Protected Overrides Sub Finalize()
          ' Do not re-create Dispose clean-up code here.
          ' Calling Dispose(false) is optimal in terms of
          ' readability and maintainability.
         Dispose(false)
   End Sub

    ' Allow your Dispose method to be called multiple times,
    ' but throw an exception if the object has been disposed.
    ' Whenever you do something with this class,
    ' check to see if it has been disposed.
   Public Sub DoSomething()
      If Me.disposed Then
         Throw New ObjectDisposedException()
      End if
   End Sub
End Class

 ' Design pattern for a derived class.
 ' Note that this derived class inherently implements the
 ' IDisposable interface because it is implemented in the base class.
Public Class MyResourceWrapper
   Inherits BaseResource

    ' A managed resource that you add in this derived class.
   private addedManaged As ManagedResource
    ' A native unmanaged resource that you add in this derived class.
   private addedNative As NativeResource
    ' Track whether Dispose has been called.
   Private disposed As Boolean = False

    ' Constructor for the MyResourceWrapper object.
   Public Sub New()
      MyBase.New()
       ' Insert appropriate constructor code here for the
       ' added resources.
   End Sub

   Protected Overloads Overrides Sub Dispose(disposing As Boolean)
      If Not (Me.disposed) Then
         Try
            If disposing Then
               ' Release the managed resources you added in
               ' this derived class here.
              addedManaged.Dispose()
            End If
             ' Release the native unmanaged resources you added
             ' in this derived class here.
            CloseHandle(addedNative)
            Me.disposed = true
         Finally
             ' Call Dispose on your base class.
            MyBase.Dispose(disposing)
         End Try
      End If
   End Sub
End Class
 ' This derived class does not have a Finalize method
 ' or a Dispose method without parameters because it
 ' inherits them from the base class.
// Design pattern for the base class.
// By implementing IDisposable, you are announcing that instances
// of this type allocate scarce resources.
public class BaseResource: IDisposable
{
   // Pointer to an external unmanaged resource.
   private IntPtr handle;
   // Other managed resource this class uses.
   private Component Components;
   // Track whether Dispose has been called.
   private bool disposed = false;

   // Constructor for the BaseResource object.
   public BaseResource()
   {
      // Insert appropriate constructor code here.
   }

   // Implement IDisposable.
   // Do not make this method virtual.
   // A derived class should not be able to override this method.
   public void Dispose()
   {
      Dispose(true);
      // Take yourself off the Finalization queue
      // to prevent finalization code for this object
      // from executing a second time.
      GC.SuppressFinalize(this);
   }

   // Dispose(bool disposing) executes in two distinct scenarios.
   // If disposing equals true, the method has been called directly
   // or indirectly by a user 's code. Managed and unmanaged resources
   // can be disposed.
   // If disposing equals false, the method has been called by the
   // runtime from inside the finalizer and you should not reference
   // other objects. Only unmanaged resources can be disposed.
   protected virtual void Dispose(bool disposing)
   {
      // Check to see if Dispose has already been called.
      if(!this.disposed)
      {
         // If disposing equals true, dispose all managed
         // and unmanaged resources.
         if(disposing)
         {
            // Dispose managed resources.
            Components.Dispose();
         }
         // Release unmanaged resources. If disposing is false,
         // only the following code is executed.
         CloseHandle(handle);
         handle = IntPtr.Zero;
         // Note that this is not thread safe.
         // Another thread could start disposing the object
         // after the managed resources are disposed,
         // but before the disposed flag is set to true.
         // If thread safety is necessary, it must be
         // implemented by the client.

      }
      disposed = true;
   }

   // Use C# destructor syntax for finalization code.
   // This destructor will run only if the Dispose method
   // does not get called.
   // It gives your base class the opportunity to finalize.
   // Do not provide destructors in types d

   // derived from this class.
   ~BaseResource()
   {
      // Do not re-create Dispose clean-up code here.
      // Calling Dispose(false) is optimal in terms of
      // readability and maintainability.
      Dispose(false);
   }

   // Allow your Dispose method to be called multiple times,
   // but throw an exception if the object has been disposed.
   // Whenever you do something with this class,
   // check to see if it has been disposed.
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}

// Design pattern for a derived class.
// Note that this derived class inherently implements the
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
   // A managed resource that you add in this derived class.
   private ManagedResource addedManaged;
   // A native unmanaged resource that you add in this derived class.
   private NativeResource addedNative;
   private bool disposed = false;

  // Constructor for this object.
   public MyResourceWrapper()
   {
      // Insert appropriate constructor code here.
   }

   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {
               // Release the managed resources you added in
               // this derived class here.
               addedManaged.Dispose();
            }
            // Release the native unmanaged resources you added
            // in this derived class here.
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            // Call Dispose on your base class.
            base.Dispose(disposing);
         }
      }
   }
}

// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.

实作 Close 方法

对於呼叫 Close 方法比呼叫 Dispose 方法来得更自然的型别,请将公用 Close 方法加入至基底型别中。Close 方法会接着呼叫不含参数的 Dispose 方法,以执行正确的清除作业。下列程式码范例是在说明 Close 方法。

' Do not make this method Overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
    ' Calls the Dispose method without parameters.
   Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
   // Calls the Dispose method without parameters.
   Dispose();
}

请参阅

GC.SuppressFinalize 方法 | 使用 C# 和 Managed Extensions for C++ 解构函式语法 | 覆写 Finalize 方法

转载自:http://msdn.microsoft.com/library/cht/default.asp?url=/library/CHT/cpguide/html/cpconimplementingdisposemethod.asp