Finalize 方法
本文章是由機器翻譯。 將指標移到文章內的文字上方即可查看原文。 其他資訊。
譯文
原文

Object.Finalize 方法 ()

 

允許物件在記憶體回收進行回收之前,嘗試釋放資源並執行其他清除作業。

命名空間:   System
組件:  mscorlib (在 mscorlib.dll 中)

protected virtual void Finalize()

Finalize 方法用來在終結物件之前,目前物件所擁有的 unmanaged 資源上執行清除作業。此方法受到保護,因此只能透過這個類別或衍生類別存取。

本節內容:

Object 類別會提供任何實作 Finalize 方法,以及記憶體回收行程不會標記型別衍生自 Object 最終處理除非它們覆寫 Finalize 方法。

如果型別會覆寫 Finalize 方法中,記憶體回收行程就會將每個執行個體類型的項目加入至稱為最終處理佇列的內部結構。最終處理佇列包含 managed 堆積記憶體回收行程回收其記憶體之前,必須執行其最終處理程式碼中的所有物件的項目。然後呼叫記憶體回收行程 Finalize 在下列情況下自動的方法:

  • 記憶體回收行程已探索的物件是無法存取,除非呼叫物件必須進行最終處理之後 GC.SuppressFinalize 方法。

  • 在關閉應用程式定義域,除非該物件是豁免最終處理。在關機期間完成甚至仍然可以存取的物件。

Finalize 會自動呼叫一次在給定的執行個體,除非物件重新註冊使用一種機制,例如 GC.ReRegisterForFinalizeGC.SuppressFinalize 方法尚未後續呼叫。

Finalize 作業具有下列限制:

  • 當完成項執行的確切時間是未定義。若要確保決定性的資源釋放的執行個體的類別,實作 Close 方法或提供 IDisposable.Dispose 實作。

  • 即使其中一個物件參考其他兩個物件的完成項不保證任何特定順序執行。也就是說,如果物件 A 具有物件 B 的參考,而且都具有完成項,物件 B 可能已完成的物件的完成項啟動時。

  • 未指定的完成項執行所在的執行緒。

Finalize 方法可能不會執行到完成為止,或可能完全無法執行下列的例外情況:

  • 如果另一個完成項會無限期地封鎖 (進入無限迴圈,在嘗試取得鎖定,它可以永遠不會取得,依此類推)。因為執行階段會嘗試完成項執行到完成為止,其他完成項可能不會呼叫如果完成項區塊無限期。

  • 如果處理程序結束,而不讓執行階段有機會以清除。在此情況下,執行階段的第一次通知的處理序終止是 DLL_PROCESS_DETACH 通知。

執行階段會繼續在關機期間最終處理物件時才會持續減少可完成的物件數目。

如果 Finalize 或覆寫 Finalize 擲回例外狀況,以及執行階段不會覆寫預設原則的應用程式所裝載,執行階段會終止程序,並沒有使用中 try/finally 區塊或完成設定式會執行。如果完成項無法釋放或終結資源,這種行為可確保處理程序完整性。

應該覆寫 Finalize 類別使用 unmanaged 的資源,例如檔案控制代碼或資料庫必須在時使用這些 managed 的物件會被捨棄在記憶體回收期間釋放的連接。

System_CAPS_important重要事項

如果 SafeHandle 都有提供物件包裝您的 unmanaged 的資源、 建議的替代方案是實作使用安全控制代碼的處置模式並不會覆寫 Finalize如需詳細資訊,請參閱 SafeHandle 替代方法 > 一節。

Object.Finalize 方法則會根據預設,不執行任何動作,但是您應該覆寫 Finalize 只有在有需要,而且只釋放 unmanaged 的資源。記憶體回收可能會花更長,如果在完成執行作業,因為它需要至少兩個記憶體回收。此外,您應該覆寫 Finalize 方法的參考型別。Common language runtime 只完成參考型別。它會忽略實值型別上的完成項。

每個實作 Finalize 中衍生型別必須呼叫其基底類型實作 Finalize這是應用程式的程式碼允許呼叫的唯一情況 Finalize

System_CAPS_note注意事項

C# 編譯器不允許您覆寫 Finalize 方法。相反地,藉由實作提供完成項 為您的類別。C# 解構函式會自動呼叫其基底類別的解構函式。

Visual c + + 也提供自己的語法來實作 Finalize 方法。如需詳細資訊,請參閱 「 解構函式和完成項 」 一節 Gewusst wie: Definieren und Verarbeiten von Klassen und Strukturen (C++/CLI)

因為記憶體回收是不具決定性的所以您不知道記憶體回收行程執行完成時。若要釋放資源,您也可以選擇實作 和 IDisposable 介面。 IDisposable.Dispose 實作只能呼叫類別的取用者釋放 unmanaged 的資源,而且您可以使用 Finalize 方法來釋放 unmanaged 的資源的 Dispose 不會呼叫方法。

Finalize 它已清除在記憶體回收期間之後,可以採取幾乎任何的動作,包括 resurrecting 物件 (也就,讓物件能夠存取一次)。不過,此物件可以只復活一次。 Finalize 無法恢復物件在記憶體回收期間呼叫。沒有一個動作,您的實作 Finalize 絕對不應該接受: 應該永遠不會擲回例外狀況。

建立可靠的完成項通常很困難,因為您不能做出假設有關應用程式的狀態,而且未處理系統例外狀況時,例如 OutOfMemoryExceptionStackOverflowException 終止完成項。除了實作類別的完成項來釋放 unmanaged 的資源,您可以使用衍生自物件 System.Runtime.InteropServices.SafeHandle 類別來包裝您的 unmanaged 的資源,然後再實作沒有完成項的處置模式。.NET Framework 會提供下列類別中的 Microsoft.Win32 衍生自的命名空間 System.Runtime.InteropServices.SafeHandle

下列範例會使用 使用安全控制代碼,而不是覆寫 Finalize 方法。它會定義 FileAssociation 包裝處理具有特定副檔名的檔案的應用程式相關的登錄資訊的類別。兩個登錄控點,以傳回 out 參數由 Windows RegOpenKeyEx 函式呼叫會傳遞至 SafeRegistryHandle 建構函式。類型的保護 Dispose 然後方法會呼叫 SafeRegistryHandle.Dispose 方法來釋放這些兩個控制代碼。

using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;

public class FileAssociationInfo : IDisposable
{
   // Private variables.
   private String ext;
   private String openCmd;
   private String args;
   private SafeRegistryHandle hExtHandle, hAppIdHandle;

   // Windows API calls.
   [DllImport("advapi32.dll", CharSet= CharSet.Auto, SetLastError=true)]
   private static extern int RegOpenKeyEx(IntPtr hKey, 
                  String lpSubKey, int ulOptions, int samDesired,
                  out IntPtr phkResult);
   [DllImport("advapi32.dll", CharSet= CharSet.Unicode, EntryPoint = "RegQueryValueExW",
              SetLastError=true)]
   private static extern int RegQueryValueEx(IntPtr hKey,
                  string lpValueName, int lpReserved, out uint lpType, 
                  string lpData, ref uint lpcbData);   
   [DllImport("advapi32.dll", SetLastError = true)]
   private static extern int RegSetValueEx(IntPtr hKey, [MarshalAs(UnmanagedType.LPStr)] string lpValueName,
                  int Reserved, uint dwType, [MarshalAs(UnmanagedType.LPStr)] string lpData,
                  int cpData);
   [DllImport("advapi32.dll", SetLastError=true)]
   private static extern int RegCloseKey(UIntPtr hKey);

   // Windows API constants.
   private const int HKEY_CLASSES_ROOT = unchecked((int) 0x80000000);
   private const int ERROR_SUCCESS = 0;

    private const int KEY_QUERY_VALUE = 1;
    private const int KEY_SET_VALUE = 0x2;

   private const uint REG_SZ = 1;

   private const int MAX_PATH = 260;

   public FileAssociationInfo(String fileExtension)
   {
      int retVal = 0;
      uint lpType = 0;

      if (!fileExtension.StartsWith("."))
             fileExtension = "." + fileExtension;
      ext = fileExtension;

      IntPtr hExtension = IntPtr.Zero;
      // Get the file extension value.
      retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), fileExtension, 0, KEY_QUERY_VALUE, out hExtension);
      if (retVal != ERROR_SUCCESS) 
         throw new Win32Exception(retVal);
      // Instantiate the first SafeRegistryHandle.
      hExtHandle = new SafeRegistryHandle(hExtension, true);

      string appId = new string(' ', MAX_PATH);
      uint appIdLength = (uint) appId.Length;
      retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(), String.Empty, 0, out lpType, appId, ref appIdLength);
      if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);
      // We no longer need the hExtension handle.
      hExtHandle.Dispose();

      // Determine the number of characters without the terminating null.
      appId = appId.Substring(0, (int) appIdLength / 2 - 1) + @"\shell\open\Command";

      // Open the application identifier key.
      string exeName = new string(' ', MAX_PATH);
      uint exeNameLength = (uint) exeName.Length;
      IntPtr hAppId;
      retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), appId, 0, KEY_QUERY_VALUE | KEY_SET_VALUE,
                            out hAppId);
       if (retVal != ERROR_SUCCESS) 
         throw new Win32Exception(retVal);

      // Instantiate the second SafeRegistryHandle.
      hAppIdHandle = new SafeRegistryHandle(hAppId, true);

      // Get the executable name for this file type.
      string exePath = new string(' ', MAX_PATH);
      uint exePathLength = (uint) exePath.Length;
      retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, out lpType, exePath, ref exePathLength);
      if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);

      // Determine the number of characters without the terminating null.
      exePath = exePath.Substring(0, (int) exePathLength / 2 - 1);
      // Remove any environment strings.
      exePath = Environment.ExpandEnvironmentVariables(exePath);

      int position = exePath.IndexOf('%');
      if (position >= 0) {
         args = exePath.Substring(position);
         // Remove command line parameters ('%0', etc.).
         exePath = exePath.Substring(0, position).Trim();
      }
      openCmd = exePath;   
   }

   public String Extension
   { get { return ext; } }

   public String Open
   { get { return openCmd; } 
     set {
        if (hAppIdHandle.IsInvalid | hAppIdHandle.IsClosed)
           throw new InvalidOperationException("Cannot write to registry key."); 
        if (! File.Exists(value)) {
           string message = String.Format("'{0}' does not exist", value);
           throw new FileNotFoundException(message); 
        }
        string cmd = value + " %1";
        int retVal = RegSetValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, 
                                   REG_SZ, value, value.Length + 1);
        if (retVal != ERROR_SUCCESS)
           throw new Win32Exception(retVal);                          
     } }

   public void Dispose() 
   {
      Dispose(true);
      GC.SuppressFinalize(this);
   }   

   protected void Dispose(bool disposing)
   {
      // Ordinarily, we release unmanaged resources here; 
      // but all are wrapped by safe handles.

      // Release disposable objects.
      if (disposing) {
         if (hExtHandle != null) hExtHandle.Dispose();
         if (hAppIdHandle != null) hAppIdHandle.Dispose();
      }
   }
}

下列範例會確認 Finalize 物件,會覆寫時,會呼叫方法 Finalize 終結。請注意,在實際執行應用程式中, Finalize 會覆寫方法來釋放物件所擁有的 unmanaged 的資源。也請注意 C# 範例提供的解構函式,而不是覆寫 Finalize 方法。

using System;
using System.Diagnostics;

public class ExampleClass
{
   Stopwatch sw;

   public ExampleClass()
   {
      sw = Stopwatch.StartNew();
      Console.WriteLine("Instantiated object");
   } 

   public void ShowDuration()
   {
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        this, sw.Elapsed);
   }

   ~ExampleClass()
   {
      Console.WriteLine("Finalizing object");
      sw.Stop();
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        this, sw.Elapsed);
   }
}

public class Demo
{
   public static void Main()
   {
      ExampleClass ex = new ExampleClass();
      ex.ShowDuration();
   }
}
// The example displays output like the following:
//    Instantiated object
//    This instance of ExampleClass has been in existence for 00:00:00.0011060
//    Finalizing object
//    This instance of ExampleClass has been in existence for 00:00:00.0036294

For an additional example that overrides the Finalize method, see the GC.SuppressFinalize method.

Universal Windows Platform
自 4.5 起可用
.NET Framework
自 1.1 起可用
Portable Class Library
支援版本:portable .NET platforms
Silverlight
自 2.0 起可用
Windows Phone Silverlight
自 7.0 起可用
Windows Phone
自 8.1 起可用
回到頁首
顯示:
© 2016 Microsoft