實作 Dispose 方法

型別的 Dispose 方法應該釋放它所擁有的所有資源,它也應該藉由呼叫其基底型別 (Base Type) 之父型別的 Dispose 方法,來釋放其基底型別所擁有的所有資源。父型別的 Dispose 方法應釋放它擁有的所有資源,接著呼叫其父型別的 Dispose 方法,將此模式傳播到整個基底型別的階層架構中。若要確保資源永遠都能適當清除,Dispose 方法應該能夠被呼叫多次而不會擲回例外狀況。

Note重要資訊

C++ 程式設計人員不應該使用這個說明主題,而應該參閱 Destructors and Finalizers in Visual C++。在 .NET Framework 2.0 版中,C++ 編譯器可讓您實作決定性的資源處置,但不允許直接實作 Dispose 方法。

Dispose 方法應該呼叫它正在處置 (Dispose) 之物件的 GC.SuppressFinalize 方法。如果物件目前正在最終處理佇列,GC.SuppressFinalize 會防止它的 Finalize 方法被呼叫。請記住,執行 Finalize 方法將付出很大的效能代價。如果您的 Dispose 方法已完成執行清除物件的工作,記憶體回收行程就不需要呼叫該物件的 Finalize 方法。

Note注意事項

此處所提供的 System.GC.KeepAlive(System.Object) 方法程式碼範例,顯示積極記憶體回收如何在被回收的物件仍在執行時,即讓完成項開始執行。在冗長的 Dispose 方法結尾處呼叫 KeepAlive 方法,這是個不錯的做法。

下列程式碼範例的目的是要說明建議使用的設計模式,這種模式可為封裝 Unmanaged 資源的類別實作 Dispose 方法。.NET Framework 都是實作這種模式。

資源類別通常是從複雜的原生類別或 API 衍生而來,因此必須依照情況加以自訂。請將這個程式碼模式當做建立資源類別的起始點,並且根據您正在封裝的資源,提供必要的自訂。您不能編譯這個範例並直接在應用程式中使用它。

在這個範例中,基底類別 BaseResource 會實作一個可讓類別使用者呼叫的公用 Dispose 方法。它會接著呼叫 virtual Dispose(bool disposing) 方法 (在 Visual Basic 中為 virtual Dispose(disposing As Boolean))。根據呼叫端的識別 (Identity) 來傳遞 truefalse。物件適當的清除程式碼會在虛擬 Dispose 方法中執行。

Dispose(bool disposing) 可在兩種不同的案例中執行。如果 disposing 等於 true,表示方法是由使用者的程式碼直接或間接呼叫的,因此可以處置 Managed 和 Unmanaged 資源。如果 disposing 等於 false,表示方法是由執行階段從完成項內部呼叫的,因此只能處置 Unmanaged 資源。當物件正在執行它的最終處理程式碼時,它不可以參考其他物件,因為完成項不會依照任何特定順序執行。如果正在執行的完成項參考的另一個物件已經做過最終處理,那麼正在執行的完成項將會失敗。

基底類別提供 Finalize 方法或解構函式,做為萬一沒有呼叫 Dispose 時的防護措施。Finalize 方法會呼叫使用參數的 Dispose 方法,以傳遞 false。請勿在 Finalize 方法內重新建立 Dispose 清除程式碼。呼叫 Dispose(false) 對於程式碼的可讀性和可維護性是最佳方式。

MyResourceWrapper 類別說明了如何從使用 Dispose 實作資源管理的類別中衍生新類別的方法。MyResourceWrapper 會覆寫 virtual Dispose(bool disposing) 方法,並提供它所建立的 Managed 和 Unmanaged 資源的清除程式碼。MyResourceWrapper 也會在自己的基底類別 BaseResource 上呼叫 Dispose,以確保其基底有機會能正常地進行清除工作。請注意,衍生類別 MyResourceWrapper 並沒有不含參數的 FinalizeDispose 方法,因為它可以從其基底類別 BaseResource 繼承這兩個方法。

Note注意事項

此一範例中的 protected Dispose(bool disposing) 方法並不會強制使用執行緒安全,因為此方法無法同時從使用者執行緒和完成項執行緒呼叫。此外,使用 BaseResource 的用戶端應用程式還應該永遠不讓多個使用者執行緒同時呼叫 protected Dispose(bool disposing) 方法。應用程式或類別庫 (Class Library) 應該設計成只能讓一個執行緒擁有資源的存留期 (Lifetime),並且要在不復需要資源時才呼叫 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 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 Method
Destructors and Finalizers in Visual C++
實作 Finalize 和 Dispose 以清除 Unmanaged 資源

概念

覆寫 Finalize 方法