导出 (0) 打印
全部展开
此文章由机器翻译。 将光标移到文章的句子上,以查看原文。 更多信息。
译文
原文

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,在使用时的托管对象将在垃圾回收过程中被丢弃。

重要说明重要事项

如果 SafeHandle 对象可用包装非托管资源,建议使用的替代方法是实现通过而不是一个安全句柄中重写 Finalize的配置模式。 有关更多信息,请参见 SafeHandle 重写 一节。

Object.Finalize 方法不执行任何操作,但默认情况下,只应在必要时,重写和 Finalize 仅用于释放非托管资源。 回收内存。垃圾回收时往往需要更长的时间,如果运行,终止操作,因为它需要至少两次垃圾回收。 另外,应该重写仅引用类型的 Finalize 方法。 公共语言运行时 (CLR) 仅完成引用类型。 它将忽略值类型上的终结器。

派生类型中的每个 Finalize 实现都必须调用其基类型的 Finalize 实现。 这是唯一一种允许应用程序代码调用 Finalize 的情况。

说明说明

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

Visual C++ 为执行 Finalize 方法还提供自己的语法。 有关更多信息,请参见 如何:实例化类和结构“析构函数和终结器”一节。

由于垃圾回收是非确定性的,不明确了解垃圾回收器在运行终止。 若要立即释放资源,还可以选择 配置模式 和实现 IDisposable 接口。 IDisposable.Dispose 实现可以由类使用者调用释放非托管资源,并且,您可以使用 Finalize 方法释放非托管资源,在 Dispose 方法未调用情况下。

Finalize 可以使用几乎所有操作,包括复活对象 (也就是说再次使访问的对象),则将在垃圾回收后已被清除。 但是,对象只能复活一次;在垃圾回收过程中,不能对复活对象调用 Finalize 具有 Finalize 实现不应执行的一项操作:不应引发异常。

SafeHandle 备用方法

创建强健的终结器通常很难,因为不能对您的应用程序的状态,假设,并且,由于系统未处理异常 (如 OutOfMemoryExceptionStackOverflowException ) 终止终结器。 而不是实现类的终结器可以释放非托管资源,可以使用从 System.Runtime.InteropServices.SafeHandle 类派生包装非托管资源的对象,然后实现 Dispose 模式,没有终结器。 .NET Framework 提供从 System.Runtime.InteropServices.SafeHandle派生在 Microsoft.Win32 命名空间的以下类:

下面的示例使用带的安全句柄的 配置模式 而不是重写 Finalize 方法。 它定义包装有关该应用程序的注册表信息了一个特定文件扩展名的文件句柄的 FileAssociation 类。 out 参数返回的两个注册表窗口 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


有关重写 Finalize 方法的其他的示例,请参见 GC.SuppressFinalize 方法。

.NET Framework

受以下版本支持:4.5.2、4.5.1、4.5、4、3.5、3.0、2.0、1.1、1.0

.NET Framework Client Profile

受以下版本支持:4、3.5 SP1

可移植类库

受以下版本支持:可移植类库

适用于 Windows 应用商店应用的 .NET

受以下版本支持:Windows 8

适用于 Windows Phone 应用的 .NET

受以下版本支持:Windows Phone 8、Silverlight 8.1

Windows Phone 8.1, Windows Phone 8, Windows 8.1, Windows Server 2012 R2, Windows 8, Windows Server 2012, Windows 7, Windows Vista SP2, Windows Server 2008(不支持服务器核心角色), Windows Server 2008 R2(支持带 SP1 或更高版本的服务器核心角色;不支持 Itanium)

.NET Framework 并不是对每个平台的所有版本都提供支持。有关支持的版本的列表,请参阅.NET Framework 系统要求

社区附加资源

添加
显示:
© 2014 Microsoft