本文由机器翻译。若要查看英语原文,请勾选“英语”复选框。 也可将鼠标指针移到文本上,在弹出窗口中显示英语原文。
翻译
英语

Object.Finalize 方法 ()

 

在垃圾回收将某一对象回收前允许该对象尝试释放资源并执行其他清理操作。

命名空间:   System
程序集:  mscorlib(位于 mscorlib.dll)

protected virtual void Finalize()

Finalize方法用于执行之前对象被销毁当前对象持有的非托管资源的清理操作。 该方法受保护,并且因此仅通过此类或派生类可访问。

本节内容:

Object类提供的实现不Finalize方法和垃圾回收器将派生的类型不标记Object终止除非它们将覆盖Finalize方法。

如果类型未重写Finalize方法,则垃圾回收器会将类型的每个实例的条目添加到调用终止队列中的内部结构。 终止队列中包含垃圾回收器才能回收其内存之前,必须运行其终止代码托管堆中的所有对象的条目。 然后,垃圾回收器调用Finalize在以下情况下自动的方法︰

  • 垃圾回收器发现,一个对象不可访问,除非您通过调用从终止豁免已对象后GC.SuppressFinalize方法。

  • 在关闭应用程序域中,除非该对象是免于终止的对象。 在关闭期间,终止甚至仍是可访问的对象。

Finalize将自动调用一次在给定实例中,除非的对象重新注册通过使用一种机制,如GC.ReRegisterForFinalizeGC.SuppressFinalize尚未随后调用方法。

Finalize操作具有以下限制︰

  • 终结器执行时的确切时间不确定。 若要确保确定性释放资源,对你的类的实例实现Close方法,或者提供IDisposable.Dispose实现。

  • 两个对象的终结器不保证任何特定顺序运行即使另一个对象引用。 也就是说,如果对象 A 具有对对象 B 的引用,并且二者的终结器,对象 B 可能已经被终结的对象 A 终结器启动时。

  • 终结器运行的线程未指定。

Finalize方法可能无法运行完成,或可能根本不运行下列异常情况下︰

  • 如果另一个终结器会无限期阻止 (进入无限循环,尝试获取的锁,它可以永远不会获取,等等)。 运行时尝试运行终结器来完成,因为其他终结器可能不会调用终结器块如果无限期。

  • 如果不提供机会清理的运行时,进程将终止。 在这种情况下,运行时的第一个通知的进程是终止的一个 DLL_PROCESS_DETACH 通知。

运行时将继续完成在关闭过程的对象,仅当可终结对象数目继续减少。

如果Finalize或的重写Finalize引发异常,并且运行时不承载的应用程序将替代默认策略,运行时终止进程,且无活动try/finally执行块或终结器。 如果终结器无法释放或销毁资源,则此行为确保处理完整性。

应重写Finalize类使用非托管的资源,如文件句柄或数据库必须在垃圾回收期间放弃使用它们的托管的对象时释放的连接。

System_CAPS_important重要事项

如果SafeHandle可用对象,则包装非托管的资源,建议的替代项是实现使用安全句柄的释放模式并不会覆盖Finalize 有关详细信息,请参阅SafeHandle 备用方法部分。

Object.Finalize方法不执行任何操作默认情况下,但应重写Finalize仅当有必要,且仅释放非托管的资源。 回收内存倾向于长得多如果运行终止操作,因为它需要至少两个垃圾回收。 此外,你应该重写Finalize仅类型引用的方法。 公共语言运行时仅完成引用类型。 它将忽略有关值类型的终结器。

每个实现Finalize派生类型中,则必须调用其基类型实现Finalize 这是其中的应用程序代码可以调用仅用例Finalize

System_CAPS_note说明

C# 编译器不允许你重写Finalize方法。 相反,通过实现提供了终结器析构函数为您的类。 C# 析构函数自动调用其基类的析构函数。

Visual c + + 还提供了其自己的语法,用于实现Finalize方法。 有关详细信息,请参阅的"析构函数和终结器"部分如何︰ 定义和使用类和结构 (c + + CLI)

垃圾回收是不确定的因为你不知道确切地说时垃圾回收器执行终止。 若要释放资源立即,你还可以选择实现释放模式IDisposable接口。 IDisposable.Dispose实现可由使用者在类的释放非托管的资源,并且你可以使用Finalize方法释放非托管的资源的事件中Dispose不调用方法。

Finalize后它已被清除在垃圾回收期间,能够执行几乎任何操作,包括再现的对象 (即,使该对象可访问再次)。 但是,该对象还能够仅重新一次;Finalize在垃圾回收期间不能在复活对象上调用。 一个操作,你实现Finalize应永远不会采取︰ 它应永远不会引发异常。

创建可靠的终结器是通常难以进行,因为无法有关你的应用程序的状态作出假设以及因为未经处理的系统异常,如OutOfMemoryExceptionStackOverflowException终止终结器。 而非实现终结器为您的类释放非托管的资源,你可以使用派生自的对象System.Runtime.InteropServices.SafeHandle类来包装非托管的资源,,然后实现而无需终结器的释放模式。 .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将重写方法,以释放由对象拥有的非托管的资源。 另请注意,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 M:System.Object.Finalize method, see the M:System.GC.SuppressFinalize(System.Object) method.

通用 Windows 平台
自 8 起可用
.NET Framework
自 1.1 起可用
可移植类库
可移植 .NET 平台 中受支持
Silverlight
自 2.0 起可用
Windows Phone Silverlight
自 7.0 起可用
Windows Phone
自 8.1 起可用
返回页首
显示: