Object.Finalize メソッド

定義

オブジェクトが、ガベージ コレクションによって収集される前に、リソースの解放とその他のクリーンアップ操作の実行を試みることができるようにします。

!Object ()
~Object ();
abstract member Finalize : unit -> unit
override this.Finalize : unit -> unit
Finalize ()

次の例では、 をオーバーライドするオブジェクトが破棄されると、 メソッドが呼び 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
Imports System.Diagnostics

Public Class ExampleClass
   Dim sw As StopWatch
   
   Public Sub New()
      sw = Stopwatch.StartNew()
      Console.WriteLine("Instantiated object")
   End Sub 

   Public Sub ShowDuration()
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        Me, sw.Elapsed)
   End Sub
   
   Protected Overrides Sub Finalize()
      Console.WriteLine("Finalizing object")
      sw.Stop()
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        Me, sw.Elapsed)
   End Sub
End Class

Module Demo
   Public Sub Main()
      Dim ex As New ExampleClass()
      ex.ShowDuration()
   End Sub
End Module
' 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 してください。

注釈

メソッドは、オブジェクトが破棄される前に、現在のオブジェクトによって保持されているアンマネージ リソースに対してクリーンアップ Finalize 操作を実行するために使用されます。 メソッドは保護されるため、このクラスまたは派生クラスを通じてのみアクセスできます。

このセクションの内容は次のとおりです。

最終処理のしくみ

クラスは メソッドの実装を提供し、ガベージ コレクターは、メソッドをオーバーライドしない限り、最終処理のために から派生した型 Object Finalize Object をマーク Finalize します。

型が メソッドをオーバーライドする場合、ガベージ コレクターは、型の各インスタンスのエントリを、最終処理キューと呼ばれる内部構造体 Finalize に追加します。 最終処理キューには、ガベージ コレクターがメモリを再利用する前に最終処理コードを実行する必要があるマネージド ヒープ内のすべてのオブジェクトのエントリが含まれています。 その後、ガベージ コレクターは、次の Finalize 条件下でメソッドを自動的に呼び出します。

  • メソッドの呼び出しによってオブジェクトが最終処理から除外されていない限り、ガベージ コレクターがオブジェクトにアクセスできないと検出 GC.SuppressFinalize された後。

  • では.NET Frameworkが 終了から除外される場合を除き、アプリケーション ドメインのシャットダウン時にのみ を使用できます。 シャットダウン中は、引き続きアクセス可能なオブジェクトも最終処理されます。

Finalize は、オブジェクトが などのメカニズムを使用して再登録され、 メソッドが後で呼び出されていない限り、特定のインスタンスで 1 回だけ自動的 GC.ReRegisterForFinalize GC.SuppressFinalize に呼び出されます。

Finalize 操作には次の制限があります。

  • ファイナライザーが実行される正確な時刻は未定義です。 クラスのインスタンスのリソースを確実に確実に解放するには、 メソッドを実装するか Close 、実装を提供 IDisposable.Dispose します。

  • 2 つの オブジェクトのファイナライザーは、一方のオブジェクトがもう一方を参照している場合でも、特定の順序で実行される保証はありません。 つまり、オブジェクト A がオブジェクト B への参照を持ち、両方にファイナライザーがある場合、オブジェクト A のファイナライザーの開始時にオブジェクト B が既に終了している可能性があります。

  • ファイナライザーが実行されるスレッドは指定されていません。

メソッドが完了しないか、次の例外的な状況下で実行 Finalize されない場合があります。

  • 別のファイナライザーが無期限にブロックする場合 (無限ループに入り、取得しないロックの取得を試みるなど)。 ランタイムは完了までファイナライザーを実行しようとするので、ファイナライザーが無期限にブロックされた場合、他のファイナライザーが呼び出されない可能性があります。

  • ランタイムにクリーンアップの機会を与えることなくプロセスが終了した場合。 この場合、ランタイムによるプロセス終了の最初の通知は、DLL_PROCESS_DETACHです。

終了可能なオブジェクトの数が減少し続ける間、ランタイムはシャットダウン中にのみオブジェクトを終了し続ける。

または のオーバーライドが例外をスローし、ランタイムが既定のポリシーをオーバーライドするアプリケーションによってホストされていない場合、ランタイムはプロセスを終了し、アクティブなブロックやファイナライザーは Finalize Finalize try / finally 実行されません。 この動作により、ファイナライザーがリソースを解放または破棄できない場合に、プロセスの整合性が確保されます。

Finalize メソッドのオーバーライド

アンマネージ リソースを使用するクラス (ファイル ハンドルやデータベース接続など) をオーバーライドする必要があります。これは、それらを使用するマネージド オブジェクトがガベージ コレクション中に破棄された場合に解放する Finalize 必要があります。 ガベージ コレクターはマネージド リソースを自動的に解放するために、マネージド オブジェクトの Finalize メソッドを実装する必要があります。

重要

アンマネージ リソースをラップする オブジェクトが使用可能な場合は、安全なハンドルを使用して破棄パターンを実装し、 をオーバーライドしない方法をお SafeHandle 勧めします Finalize 。 詳細については 、「SafeHandle の代替」セクションを参照 してください。

メソッドは既定では何も実行しませんが、必要な場合にのみオーバーライドし、アンマネージ リソースのみを Object.Finalize Finalize 解放する必要があります。 少なくとも 2 つのガベージ コレクションが必要なので、最終処理操作を実行すると、メモリの再利用に時間がかかる傾向があります。 また、参照型の場合にのみ Finalize メソッドをオーバーライドする必要があります。 共通言語ランタイムは、参照型のみを終了します。 値型のファイナライザーは無視されます。

メソッドのスコープ Object.Finalize は です protected 。 クラス内でこのメソッドをオーバーライドする場合は、このスコープの範囲を維持する必要があります。 メソッドを保護することで、アプリケーションのユーザーがオブジェクトの メソッドを Finalize 直接呼び出すのを Finalize 防ぐ必要があります。

派生型の Finalize のすべての実装は、基本型の の実装を呼び出す必要があります Finalize 。 アプリケーション コードで を呼び出す場合は、これが唯一のケースです Finalize 。 オブジェクトの メソッドは、その基本クラス以外のオブジェクトでメソッド Finalize を呼び出さてはならない。 これは、共通言語ランタイムがシャットダウンされる場合など、呼び出し元のオブジェクトと呼び出された別のオブジェクトが同時にガベージ コレクションされることがあるからです。

注意

C# コンパイラでは、 メソッドをオーバーライド Finalize できない。 代わりに、クラスのデストラクターを実装してファイナ ライザー を提供します。 C# デストラクターは、その基本クラスのデストラクターを自動的に呼び出します。

Visual C++メソッドを実装するための独自の構文も提供 Finalize します。 詳細については、「方法: クラスと構造体を定義および使用する (C++/CLI)」の「デストラクターとファイナライザー」セクションを参照してください。

ガベージ コレクションは決定論的ではないので、ガベージ コレクターがいつ最終処理を実行するのか正確にはわかりません。 リソースをすぐに解放するには、破棄パターンとインターフェイス を実装 IDisposable することもできます。 実装は、アンマネージ リソースを解放するためにクラスのコンシューマーによって呼び出され、メソッドが呼び出されない場合に、 メソッドを使用してアンマネージ リソースを解放 IDisposable.Dispose Finalize Dispose できます。

Finalize は、ガベージ コレクション中にクリーンアップされた後にオブジェクトを再取得する (つまり、オブジェクトを再度アクセス可能にする) など、ほぼすべてのアクションを実行できます。 ただし、 オブジェクトは 1 回だけ再開できます。 Finalize は、ガベージ コレクション中に、再発生したオブジェクトで呼び出す必要があります。

代替手段としての SafeHandle

信頼性の高いファイナライザーの作成は、多くの場合困難です。これは、アプリケーションの状態を想定できない場合や、 や などの未処理のシステム例外がファイナライザーを終了する場合などです OutOfMemoryException StackOverflowException 。 クラスのファイナライザーを実装してアンマネージ リソースを解放する代わりに、 クラスから派生した オブジェクトを使用してアンマネージ リソースをラップし、ファイナライザーなしで破棄パターンを実装できます。 System.Runtime.InteropServices.SafeHandle 次.NET Frameworkは、 から派生した名前空間に Microsoft.Win32 次のクラスを提供します System.Runtime.InteropServices.SafeHandle

次の例では、 メソッドをオーバーライドする 代わりに、安全なハンドルを持つ破棄パターンを使用 Finalize します。 特定のファイル拡張子を持つファイルを処理するアプリケーションに関するレジストリ情報をラップ FileAssociation するクラスを定義します。 RegOpenKeyEx 関数呼び出しがコンストラクターに渡Windowsによってパラメーターとして返される 2 つのレジストリ out ハンドル SafeRegistryHandle 。 その後、型の protected Dispose メソッドは、 メソッドを呼 SafeRegistryHandle.Dispose び出して、これら 2 つのハンドルを解放します。

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(disposing: 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();
      }
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text

Public Class FileAssociationInfo : Implements IDisposable
   ' Private variables.
   Private ext As String
   Private openCmd As String
   Private args As String
   Private hExtHandle, hAppIdHandle As SafeRegistryHandle

   ' Windows API calls.
   Private Declare Unicode Function RegOpenKeyEx Lib"advapi32.dll" _
                   Alias "RegOpenKeyExW" (hKey As IntPtr, lpSubKey As String, _
                   ulOptions As Integer, samDesired As Integer, _
                   ByRef phkResult As IntPtr) As Integer
   Private Declare Unicode Function RegQueryValueEx Lib "advapi32.dll" _
                   Alias "RegQueryValueExW" (hKey As IntPtr, _
                   lpValueName As String, lpReserved As Integer, _
                   ByRef lpType As UInteger, lpData As String, _
                   ByRef lpcbData As UInteger) As Integer
   Private Declare Function RegSetValueEx Lib "advapi32.dll" _
                  (hKey As IntPtr, _
                  <MarshalAs(UnmanagedType.LPStr)> lpValueName As String, _
                  reserved As Integer, dwType As UInteger, _
                  <MarshalAs(UnmanagedType.LPStr)> lpData As String, _
                  cpData As Integer) As Integer
   Private Declare Function RegCloseKey Lib "advapi32.dll" _
                  (hKey As IntPtr) As Integer

   ' Windows API constants.
   Private Const HKEY_CLASSES_ROOT As Integer = &h80000000
   Private Const ERROR_SUCCESS As Integer = 0

   Private Const KEY_QUERY_VALUE As Integer = 1
   Private Const KEY_SET_VALUE As Integer = &h2

   Private REG_SZ As UInteger = 1

   Private Const MAX_PATH As Integer  = 260

   Public Sub New(fileExtension As String)
      Dim retVal As Integer = 0
      Dim lpType As UInteger = 0

      If Not fileExtension.StartsWith(".") Then
         fileExtension = "." + fileExtension
      End If
      ext = fileExtension

      Dim hExtension As IntPtr = IntPtr.Zero
      ' Get the file extension value.
      retVal = RegOpenKeyEx(New IntPtr(HKEY_CLASSES_ROOT), fileExtension, 0,
                            KEY_QUERY_VALUE, hExtension)
      if retVal <> ERROR_SUCCESS Then
         Throw New Win32Exception(retVal)
      End If
      ' Instantiate the first SafeRegistryHandle.
      hExtHandle = New SafeRegistryHandle(hExtension, True)

      Dim appId As New String(" "c, MAX_PATH)
      Dim appIdLength As UInteger = CUInt(appId.Length)
      retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(), String.Empty, _
                               0, lpType, appId, appIdLength)
      if retVal <> ERROR_SUCCESS Then
         Throw New Win32Exception(retVal)
      End If
      ' We no longer need the hExtension handle.
      hExtHandle.Dispose()

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

      ' Open the application identifier key.
      Dim exeName As New string(" "c, MAX_PATH)
      Dim exeNameLength As UInteger = CUInt(exeName.Length)
      Dim hAppId As IntPtr
      retVal = RegOpenKeyEx(New IntPtr(HKEY_CLASSES_ROOT), appId, 0,
                            KEY_QUERY_VALUE Or KEY_SET_VALUE, hAppId)
      If retVal <> ERROR_SUCCESS Then
         Throw New Win32Exception(retVal)
      End If

      ' Instantiate the second SafeRegistryHandle.
      hAppIdHandle = New SafeRegistryHandle(hAppId, True)

      ' Get the executable name for this file type.
      Dim exePath As New string(" "c, MAX_PATH)
      Dim exePathLength As UInteger = CUInt(exePath.Length)
      retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(), _
                               String.Empty, 0, lpType, exePath, exePathLength)
      If retVal <> ERROR_SUCCESS Then
         Throw New Win32Exception(retVal)
      End If
      ' Determine the number of characters without the terminating null.
      exePath = exePath.Substring(0, CInt(exePathLength) \ 2 - 1)

      exePath = Environment.ExpandEnvironmentVariables(exePath)
      Dim position As Integer = exePath.IndexOf("%"c)
      If position >= 0 Then
         args = exePath.Substring(position)
         ' Remove command line parameters ('%0', etc.).
         exePath = exePath.Substring(0, position).Trim()
      End If
      openCmd = exePath
   End Sub

   Public ReadOnly Property Extension As String
      Get
         Return ext
      End Get
   End Property

   Public Property Open As String
      Get
         Return openCmd
      End Get
      Set
        If hAppIdHandle.IsInvalid Or hAppIdHandle.IsClosed Then
           Throw New InvalidOperationException("Cannot write to registry key.")
        End If
        If Not File.Exists(value) Then
           Dim message As String = String.Format("'{0}' does not exist", value)
           Throw New FileNotFoundException(message)
        End If
        Dim cmd As String = value + " %1"
        Dim retVal As Integer = RegSetValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0,
                                              REG_SZ, value, value.Length + 1)
        If retVal <> ERROR_SUCCESS Then
           Throw New Win32Exception(retVal)
        End If
      End Set
   End Property

   Public Sub Dispose() _
      Implements IDisposable.Dispose
      Dispose(disposing:=True)
      GC.SuppressFinalize(Me)
   End Sub

   Protected Sub Dispose(disposing As Boolean)
      ' Ordinarily, we release unmanaged resources here
      ' but all are wrapped by safe handles.

      ' Release disposable objects.
      If disposing Then
         If hExtHandle IsNot Nothing Then hExtHandle.Dispose()
         If hAppIdHandle IsNot Nothing Then hAppIdHandle.Dispose()
      End If
   End Sub
End Class

適用対象

こちらもご覧ください