![]() |
---|
ファイナライズと C++ を使用してリソースを破棄の詳細についてを参照してくださいDestructors and Finalizers in Visual C++。 |
クラスのインスタンスは、ウィンドウ ハンドル (HWND)、データベース接続など、ランタイムで管理されないリソースに対する制御をカプセル化することがよくあります。 そのため、これらのリソースを解放する明示的な方法と暗黙的な方法の両方をサポートする必要があります。 暗黙的な制御を提供するには、オブジェクトにプロテクト Finalize を実装します (C# および C++ のデストラクター構文)。 そのオブジェクトへの有効な参照がなくなった後で、ガベージ コレクターがこのメソッドを呼び出します。
場合によっては、ガベージ コレクターがオブジェクトを解放する前に、そのオブジェクトを使用するプログラマが明示的にこれらの外部リソースを解放できるようにする必要があります。 外部リソースが不足している場合、またはパフォーマンス低下の原因となっている場合には、それらのリソースが使用されなくなったときにプログラマが明示的に解放することによって、パフォーマンスの向上を図ることができます。 明示的な制御を提供するには、IDisposable で用意されている Dispose を実装します。 オブジェクトのコンシューマーは、オブジェクトの使用を終了したら、Dispose メソッドを呼び出す必要があります。 Dispose は、オブジェクトへの他の参照が有効なときでも呼び出すことができます。
Dispose によって明示的な制御を提供した場合でも、Finalize メソッドを使用する暗黙的なクリーンアップを提供する必要があります。 Finalize は、プログラマが Dispose を呼び出さなかった場合に、リソースが永久にリークされることを防ぐためのバックアップとして提供します。
アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装の詳細については、「ガベージ コレクション」を参照してください。 次の例を実装するための基本デザイン パターンを示しています処分。 この例では System 名前空間が必要です。
' Design pattern for a base class.
Public Class Base
Implements IDisposable
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
' Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Free other state (managed objects).
disposed = True
End If
' Free your own state (unmanaged objects).
' Set large fields to null.
End If
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Dispose (False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Release managed resources.
End If
' Release unmanaged resources.
' Set large fields to null.
disposed = True
End If
' Call Dispose on your base class.
Mybase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method without parameters because it inherits
' them from the base class.
End Class
// Design pattern for a base class.
public class Base: IDisposable
{
private bool disposed = false;
//Implement IDisposable.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~Base()
{
// Simply call Dispose(false).
Dispose (false);
}
}
// Design pattern for a derived class.
public class Derived: Base
{
private bool disposed = false;
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Release managed resources.
}
// Release unmanaged resources.
// Set large fields to null.
// Call Dispose on your base class.
disposed = true;
}
base.Dispose(disposing);
}
// The derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
}
別の方法を表示するには、前の例の次のコードを展開処分 が呼び出されると Finalizeと呼ばれます。 破棄のパターンの段階の出力をコンソールに追跡されます。 割り当てとアンマネージ リソースの解放は、派生クラスで処理されます。
Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
' Design pattern for a base class.
Public MustInherit Class Base
Implements IDisposable
Private disposed as Boolean = false
Private instName As String
Private trackingList As List(Of Object)
Public Sub New(instanceName As String, tracking As List(Of Object))
MyClass.instName = instanceName
trackingList = tracking
trackingList.Add(Me)
End Sub
Public ReadOnly Property InstanceName() As String
Get
Return instName
End Get
End Property
'Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Console.WriteLine(vbNewLine + "[{0}].Base.Dispose()", instName)
Dispose(true)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Free other state (managed objects).
Console.WriteLine("[{0}].Base.Dispose(true)", instName)
trackingList.Remove(Me)
Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
instanceName, MyClass.GetHashCode())
Else
Console.WriteLine("[{0}].Base.Dispose(false)", instName)
End If
disposed = True
End If
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Console.WriteLine(vbNewLine + "[{0}].Base.Finalize()", instName)
Dispose(False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
Private disposed as Boolean = false
Private umResource As IntPtr
Public Sub New(instanceName As String, tracking As List(Of Object))
MyBase.New(instanceName, tracking)
' Save the instance name as an unmanaged resource
umResource = Marshal.StringToCoTaskMemAuto(instanceName)
End Sub
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName)
' Release managed resources.
Else
Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName)
End If
' Release unmanaged resources.
If umResource <> IntPtr.Zero
Marshal.FreeCoTaskMem(umResource)
Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}", _
InstanceName, umResource.ToInt64())
umResource = IntPtr.Zero
End If
disposed = True
End If
' Call Dispose in the base class.
MyBase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method without parameters because it inherits
' them from the base class.
End Class
Public Class TestDisposal
Public Shared Sub Main()
Dim tracking As New List(Of Object)()
' Dispose is not called, Finalize will be called later.
Using Nothing
Console.WriteLine(vbNewLine + "Disposal Scenario: #1" + vbNewLine)
Dim d3 As New Derived("d1", tracking)
End Using
' Dispose is implicitly called in the scope of the using statement.
Using d1 As New Derived("d2", tracking)
Console.WriteLine(vbNewLine + "Disposal Scenario: #2" + vbNewLine)
End Using
' Dispose is explicitly called.
Using Nothing
Console.WriteLine(vbNewLine + "Disposal Scenario: #3" + vbNewLine)
Dim d2 As New Derived("d3", tracking)
d2.Dispose()
End Using
' Again, Dispose is not called, Finalize will be called later.
Using Nothing
Console.WriteLine(vbNewLine + "Disposal Scenario: #4" + vbNewLine)
Dim d4 As New Derived("d4", tracking)
End Using
' List the objects remaining to dispose.
Console.WriteLine(vbNewLine + "Objects remaining to dispose = {0:d}", tracking.Count)
For Each dd As Derived in tracking
Console.WriteLine(" Reference Object: {0:s}, {1:x16}",
dd.InstanceName, dd.GetHashCode())
Next dd
' Queued finalizers will be exeucted when Main() goes out of scope.
Console.WriteLine(vbNewLine + "Dequeueing finalizers...")
End Sub
End Class
' The program will display output similar to the following:
'
' Disposal Scenario: #1
'
'
' Disposal Scenario: #2
'
'
' [d2].Base.Dispose()
' [d2].Derived.Dispose(true)
' [d2] Unmanaged memory freed at 00000000001ce420
' [d2].Base.Dispose(true)
' [d2] Removed from tracking list: 0000000002bf8098
'
' Disposal Scenario: #3
'
'
' [d3].Base.Dispose()
' [d3].Derived.Dispose(true)
' [d3] Unmanaged memory freed at 00000000001ce420
' [d3].Base.Dispose(true)
' [d3] Removed from tracking list: 0000000000bb8560
'
' Disposal Scenario: #4
'
'
' Objects remaining to dispose = 2
' Reference Object: d1, 000000000297b065
' Reference Object: d4, 0000000003553390
'
' Dequeueing finalizers...
'
' [d4].Base.Finalize()
' [d4].Derived.Dispose(false)
' [d4] Unmanaged memory freed at 00000000001ce420
' [d4].Base.Dispose(false)
'
' [d1].Base.Finalize()
' [d1].Derived.Dispose(false)
' [d1] Unmanaged memory freed at 00000000001ce3f0
' [d1].Base.Dispose(false)
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
// Design pattern for a base class.
public abstract class Base : IDisposable
{
private bool disposed = false;
private string instanceName;
private List<object> trackingList;
public Base(string instanceName, List<object> tracking)
{
this.instanceName = instanceName;
trackingList = tracking;
trackingList.Add(this);
}
public string InstanceName
{
get
{
return instanceName;
}
}
//Implement IDisposable.
public void Dispose()
{
Console.WriteLine("\n[{0}].Base.Dispose()", instanceName);
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
Console.WriteLine("[{0}].Base.Dispose(true)", instanceName);
trackingList.Remove(this);
Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
instanceName, this.GetHashCode());
}
else
{
Console.WriteLine("[{0}].Base.Dispose(false)", instanceName);
}
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~Base()
{
// Simply call Dispose(false).
Console.WriteLine("\n[{0}].Base.Finalize()", instanceName);
Dispose(false);
}
}
// Design pattern for a derived class.
public class Derived : Base
{
private bool disposed = false;
private IntPtr umResource;
public Derived(string instanceName, List<object> tracking) :
base(instanceName, tracking)
{
// Save the instance name as an unmanaged resource
umResource = Marshal.StringToCoTaskMemAuto(instanceName);
}
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName);
// Release managed resources.
}
else
{
Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName);
}
// Release unmanaged resources.
if (umResource != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(umResource);
Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}",
InstanceName, umResource.ToInt64());
umResource = IntPtr.Zero;
}
disposed = true;
}
// Call Dispose in the base class.
base.Dispose(disposing);
}
// The derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
}
public class TestDisposal
{
public static void Main()
{
List<object> tracking = new List<object>();
// Dispose is not called, Finalize will be called later.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #1\n");
Derived d3 = new Derived("d1", tracking);
}
// Dispose is implicitly called in the scope of the using statement.
using (Derived d1 = new Derived("d2", tracking))
{
Console.WriteLine("\nDisposal Scenario: #2\n");
}
// Dispose is explicitly called.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #3\n");
Derived d2 = new Derived("d3", tracking);
d2.Dispose();
}
// Again, Dispose is not called, Finalize will be called later.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #4\n");
Derived d4 = new Derived("d4", tracking);
}
// List the objects remaining to dispose.
Console.WriteLine("\nObjects remaining to dispose = {0:d}", tracking.Count);
foreach (Derived dd in tracking)
{
Console.WriteLine(" Reference Object: {0:s}, {1:x16}",
dd.InstanceName, dd.GetHashCode());
}
// Queued finalizers will be exeucted when Main() goes out of scope.
Console.WriteLine("\nDequeueing finalizers...");
}
}
// The program will display output similar to the following:
//
// Disposal Scenario: #1
//
//
// Disposal Scenario: #2
//
//
// [d2].Base.Dispose()
// [d2].Derived.Dispose(true)
// [d2] Unmanaged memory freed at 000000000034e420
// [d2].Base.Dispose(true)
// [d2] Removed from tracking list: 0000000002bf8098
//
// Disposal Scenario: #3
//
//
// [d3].Base.Dispose()
// [d3].Derived.Dispose(true)
// [d3] Unmanaged memory freed at 000000000034e420
// [d3].Base.Dispose(true)
// [d3] Removed from tracking list: 0000000000bb8560
//
// Disposal Scenario: #4
//
//
// Objects remaining to dispose = 2
// Reference Object: d1, 000000000297b065
// Reference Object: d4, 0000000003553390
//
// Dequeueing finalizers...
//
// [d4].Base.Finalize()
// [d4].Derived.Dispose(false)
// [d4] Unmanaged memory freed at 000000000034e420
// [d4].Base.Dispose(false)
//
// [d1].Base.Finalize()
// [d1].Derived.Dispose(false)
// [d1] Unmanaged memory freed at 000000000034e3f0
// [d1].Base.Dispose(false)
デザイン パターンの実装を示すコード例をFinalizeと 処分を参照してください 「Dispose メソッドの実装.
Dispose メソッド名のカスタマイズ
場合によっては、Dispose ではなく、ドメイン固有の名前を付ける方が適切なこともあります。 たとえば、ファイルのカプセル化では、メソッド名として Close を使用した方が適切です。 この場合は、Dispose をプライベートに実装し、Dispose を呼び出すパブリックな Close メソッドを作成します。 このパターンを説明するコード例を次に示します。 Close は、ドメインに適したメソッド名に置き換えることができます。 この例では System 名前空間が必要です。
' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
' Call the Dispose method with no parameters.
Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
// Call the Dispose method with no parameters.
Dispose();
}
Finalize
次の規則は、Finalize メソッドの使用方法のガイドラインを示しています。
終了処理が必要なオブジェクトでだけ Finalize を実装します。 Finalize メソッドを実装すると、パフォーマンスが低下します。
Finalize メソッドが必要な場合は、クラスを使用するときに Finalize メソッドを呼び出すことによるパフォーマンスの低下を回避できるように、IDisposable を実装することを検討する必要があります。
Finalize メソッドの参照可能性は限定します。 つまり、public ではなく、protected にします。
オブジェクトの Finalize メソッドは、そのオブジェクトが保持しているすべての外部リソースを解放する必要があります。 さらに、Finalize メソッドは、そのオブジェクトが保持しているリソースだけを解放する必要があります。 Finalize メソッドは、他のオブジェクトは参照できません。
オブジェクトの基本クラス以外のオブジェクトに対しては Finalize メソッドを直接呼び出さないようにします。 これは、C# プログラミング言語では正しい操作ではありません。
オブジェクトの Finalize メソッドから基本クラスの Finalize メソッドを呼び出します。
メモ
基本クラスの Finalize メソッドは、C# および C++ のデストラクター構文によって自動的に呼び出されます。
Dispose
次の規則は、Dispose メソッドの使用方法のガイドラインを示しています。
Dispose のデザイン パターンは、明示的に解放する必要があるリソースをカプセル化する型に対して実装します。 パブリックな Dispose メソッドを呼び出すことによって、外部リソースを解放できるようにします。
Dispose のデザイン パターンは、基本型そのものがリソースを保持するかどうかにかかわらず、一般にリソースを保持する派生型を持つ基本型に対して実装します。 基本型に Close メソッドがある場合は、多くの場合、Dispose を実装する必要があります。 この場合は、その基本型に対して Finalize メソッドを実装しないでください。 Finalize は、クリーンアップが必要なリソースを保持する派生型で実装する必要があります。
Dispose メソッドは、その型が所有している破棄可能なすべてのリソースを解放します。
インスタンスで Dispose が呼び出された後、GC.SuppressFinalize を呼び出すことにより、Finalize メソッドが実行されることがないようにします。 まれに、この規則の例外として、Dispose で処理されない作業を Finalize で実行することもあります。
IDisposable を実装する場合は、基本クラスの Dispose メソッドを呼び出します。
Dispose が呼び出されることを前提にはしないでください。 Dispose が呼び出されなかった場合は、型が所有するアンマネージ リソースも Finalize メソッドで解放する必要があります。
リソースが既に破棄されている場合は、この型 (Dispose 以外) のインスタンス メソッドから ObjectDisposedException をスローします。 Dispose メソッドは、例外をスローせずに複数回呼び出すことができなければならないため、Dispose メソッドにはこの規則は適用されません。
基本型の階層構造を通じて、Dispose 呼び出しを反映させます。 Dispose メソッドは、対象のオブジェクトおよびそのオブジェクトが所有するすべてのオブジェクトが保持する全リソースを解放する必要があります。 たとえば、Stream と Encoding を保持する TextReader のようなオブジェクトを作成できますが、この Stream と Encoding は、いずれも TextReader によって開発者の知らない間に作成されます。 さらに、Stream と Encoding は、いずれも外部リソースを取得します。 TextReader に対して Dispose メソッドを呼び出す場合は、それらの外部リソースも解放できるように、Stream と Encoding に対しても Dispose が呼び出されるようにしておく必要があります。
オブジェクトに対して Dispose メソッドを呼び出した後は、そのオブジェクトを使用できないようにすることを検討してください。 既に破棄されたオブジェクトの再作成は、実装するのが困難なパターンです。
例外がスローされることなく、Dispose メソッドを複数回呼び出すことができるようにします。 最初の呼び出し以外では、何もしないようにします。
Portions Copyright 2005 Microsoft Corporation. All rights reserved.
Portions Copyright Addison-Wesley Corporation. All rights reserved.
設計ガイドラインの詳細についてを参照してください、「フレームワークの設計ガイドライン。規則、慣用句、および再利用可能なパターン。ネット ライブラリ」本クシシュトフ Cwalina、ブラッド エイブラムス、アスキー、2005 年発表しました。