一部の人々がFinalize
メソッドよりもDispose
メソッドを使用するのはなぜですか?
どのような状況で、Finalize
メソッドよりもDispose
メソッドを使用しますか?
他の人は既にDispose
とFinalize
の違いを扱っています(ただし、Finalize
メソッドは言語仕様ではデストラクタと呼ばれています)。 Finalize
メソッドが便利です。
一部のタイプは、使いやすい方法で使い捨てリソースをカプセル化し、1回のアクションでそれらを破棄します。一般的な使用法は次のようなものです:オープン、読み取りまたは書き込み、クローズ(破棄)。 using
コンストラクトに非常によく適合します。
その他は少し難しいです。インスタンスのWaitEventHandles
は、あるスレッドから別のスレッドにシグナルを送るために使用されるため、このようには使用されません。質問は、これらのDispose
を呼び出す必要がある人になりますか?これらのタイプのセーフガードとして、Finalize
メソッドを実装します。これにより、インスタンスがアプリケーションによって参照されなくなったときにリソースが確実に破棄されます。
ファイナライザメソッドは、オブジェクトがガベージコレクションされるときに呼び出され、いつ発生するか保証はありません(強制することはできますが、パフォーマンスが低下します)。
一方、Dispose
メソッドは、クラスを作成したコードによって呼び出されるため、取得したリソース(アンマネージデータ、データベース接続、ファイルハンドルなど)をクリーンアップして解放できます。コードはオブジェクトで実行されます。
標準的な方法では、IDisposable
およびDispose
を実装して、オブジェクトをusing
ステートメントで使用できるようにします。 using(var foo = new MyObject()) { }
など。そして、ファイナライザでは、呼び出しコードがあなたを破棄するのを忘れた場合に備えて、Dispose
を呼び出します。
Finalizeは、オブジェクトを回収するときにガベージコレクターによって呼び出されるバックストップメソッドです。破棄は、GCがオブジェクトに到達するまで無期限に保持するのではなく、不要になったときに貴重なネイティブリソース(ウィンドウハンドル、データベース接続など)を解放するためにアプリケーションによって呼び出される「確定的クリーンアップ」メソッドです。
オブジェクトのユーザーとして、常にDisposeを使用します。ファイナライズはGC用です。
クラスの実装者として、破棄すべき管理対象リソースを保持している場合、破棄を実装します。ネイティブリソースを保持している場合は、DisposeとFinalizeの両方を実装し、両方がネイティブリソースを解放する共通メソッドを呼び出します。これらのイディオムは通常、Dispose呼び出しをtrueで、Finalize呼び出しをfalseで呼び出すDispose(bool disposing)メソッドを介して結合されます。このメソッドは常にネイティブリソースを解放し、破棄パラメーターをチェックします。それが真の場合、マネージリソースを破棄し、GC.SuppressFinalizeを呼び出します。
最終化
protected
またはpublic
ではなく、常にprivate
である必要があります。これにより、メソッドをアプリケーションのコードから直接呼び出すことができず、同時にbase.Finalize
メソッドを呼び出すことができます。破棄
IDisposable
を実装しますDispose
メソッドを呼び出した後、オブジェクトが使用不可になっていることを確認してください。言い換えると、Dispose
メソッドが呼び出された後にオブジェクトを使用しないでください。Dispose
タイプで処理が完了したら、IDisposable
を呼び出しますDispose
がエラーを発生させることなく複数回呼び出されることを許可します。GC.SuppressFinalize
メソッドを使用して、Dispose
メソッド内からファイナライザーへの以降の呼び出しを抑制しますDispose
メソッド内から例外をスローしないようにします廃棄/最終パターン
Dispose
とFinalize
の両方を実装することをお勧めします。開発者がFinalize
メソッドを明示的に呼び出さない場合でも、オブジェクトがガベージコレクションされると、Dispose
実装が実行され、リソースが解放されます。Finalize
メソッドとDispose
メソッドでアンマネージリソースをクリーンアップします。さらに、Dispose
メソッドから、そのクラス内にコンポーネントとして管理されていないすべての.NETオブジェクト(メンバーとしてアンマネージリソースがある)のDispose
メソッドを呼び出します。このオブジェクトが使用されなくなると、GCによってファイナライズが呼び出されます。
Disposeは、このクラスのユーザーがリソースを解放するために呼び出すことができる通常のメソッドです。
ユーザーがDisposeの呼び出しを忘れた場合、およびクラスにFinalizeが実装されている場合、GCは確実に呼び出されるようにします。
本MCSD Certification Toolkit(試験70-483)pag 193からいくつかのキーがあります:
デストラクタ≈(ほぼ等しい)base.Finalize()、デストラクタは、デストラクタのコードを実行してからベースクラスのFinalizeメソッドを呼び出す、Finalizeメソッドのオーバーライドバージョンに変換されます。 GCに依存しているため、いつ呼び出されるかを知ることができない完全に非決定的です。
クラスにマネージリソースとアンマネージリソースが含まれていない場合、IDisposableを実装する必要はなく、デストラクタが必要です。
クラスに管理対象リソースのみがある場合、IDisposableを実装する必要がありますが、デストラクタは必要ありません。 (デストラクタが実行されると、管理オブジェクトがまだ存在していることを確認できないため、いずれにしてもDisposeメソッドを呼び出すことはできません。)
クラスにアンマネージリソースのみがある場合、IDisposableを実装する必要があり、プログラムがDisposeを呼び出さない場合に備えてデストラクタが必要です。
Disposeメソッドは、複数回実行しても安全である必要があります。これを実現するには、変数を使用して、それが以前に実行されたかどうかを追跡します。
Disposeメソッドは、マネージリソースとアンマネージリソースの両方を解放する必要があります。
デストラクタはアンマネージリソースのみを解放する必要があります。 (デストラクタが実行されると、管理オブジェクトがまだ存在していることを確認できないため、いずれにしてもDisposeメソッドを呼び出すことはできません。)
リソースを解放した後、デストラクタはGC.SuppressFinalizeを呼び出す必要がありますなので、オブジェクトはファイナライズキューをスキップできます。
アンマネージリソースとマネージリソースを含むクラスの実装の例:
using System;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
FreeResources(true);
}
// Destructor to clean up unmanaged resources
// but not managed resources.
~DisposableClass()
{
FreeResources(false);
}
// Keep track if whether resources are already freed.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(bool freeManagedResources)
{
Console.WriteLine(Name + ": FreeResources");
if (!ResourcesAreFreed)
{
// Dispose of managed resources if appropriate.
if (freeManagedResources)
{
// Dispose of managed resources here.
Console.WriteLine(Name + ": Dispose of managed resources");
}
// Dispose of unmanaged resources here.
Console.WriteLine(Name + ": Dispose of unmanaged resources");
// Remember that we have disposed of resources.
ResourcesAreFreed = true;
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
}
}
99%の時間、心配する必要はないはずです。 :)しかし、オブジェクトが非管理リソース(ウィンドウハンドル、ファイルハンドルなど)への参照を保持している場合、管理オブジェクトがそれらのリソースを解放する方法を提供する必要があります。 Finalizeは、リソースの解放を暗黙的に制御します。ガベージコレクターによって呼び出されます。破棄は、リソースのリリースを明示的に制御する方法であり、直接呼び出すことができます。
Garbage Collection の主題について学ぶべきことはまだたくさんありますが、それは出発点です。
ファイナライザは暗黙的なクリーンアップ用です-クラスがリソースを管理する場合は常にこれを使用する必要があります。リソースは絶対にmustクリーンアップする必要があります。 ..
ファイナライザを正しく実装することは悪名高く困難であり、可能な限り回避する必要があります-SafeHandle
クラス(.Net v2.0以降で使用可能)は、ファイナライザを実装する必要がほとんどないことを意味します。
IDisposable
インターフェースは明示的なクリーンアップ用であり、より一般的に使用されます-ユーザーがオブジェクトの使用を終了するたびにリソースを明示的に解放またはクリーンアップできるようにするには、これを使用する必要があります。
ファイナライザがある場合は、IDisposable
インターフェイスも実装して、オブジェクトがガベージコレクションされた場合よりも早くリソースを明示的に解放できるようにする必要があることに注意してください。
DG Update:Dispose、Finalization、およびResource Management を参照してください。ファイナライザーとIDisposable
に関する推奨事項の最良かつ最も完全なセットであると考えられるものについては。
要約は-
また、別の違いは-Dispose()実装では、マネージリソースも解放する必要がありますですが、ファイナライザーでは実行しないでください。これは、オブジェクトによって参照される管理対象リソースが、ファイナライズの準備が整う前にすでにクリーンアップされている可能性が非常に高いためです。
アンマネージリソースを使用するクラスのベストプラクティスは、開発者がオブジェクトを明示的に破棄するのを忘れた場合のフォールバックとして使用されるDispose()メソッドとFinalizerの両方を定義することです。どちらも共有メソッドを使用して、管理対象および管理対象外のリソースをクリーンアップできます。
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// Call our shared helper method.
// Specifying "true" signifies that the object user triggered the cleanup.
CleanUp(true);
// Now suppress finalization to make sure that the Finalize method
// doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);
}
private void CleanUp(bool disposing)
{
// Be sure we have not already been disposed!
if (!this.disposed)
{
// If disposing equals true i.e. if disposed explicitly, dispose all
// managed resources.
if (disposing)
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
// the below is called the destructor or Finalizer
~ClassWithDisposeAndFinalize()
{
// Call our shared helper method.
// Specifying "false" signifies that the GC triggered the cleanup.
CleanUp(false);
}
私が知っている最高の例。
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
クラスインスタンスは、多くの場合、ウィンドウハンドル(HWND)、データベース接続など、ランタイムによって管理されていないリソースの制御をカプセル化します。したがって、これらのリソースを解放するための明示的な方法と暗黙的な方法の両方を提供する必要があります。保護されたFinalizeメソッドをオブジェクトに実装することにより、暗黙的な制御を提供します(C#のデストラクタ構文とC++のマネージ拡張機能)。ガベージコレクターは、オブジェクトへの有効な参照がなくなった後のある時点でこのメソッドを呼び出します。場合によっては、オブジェクトを使用するプログラマに、ガベージコレクターがオブジェクトを解放する前にこれらの外部リソースを明示的に解放する機能を提供することができます。外部リソースが不足しているか高価である場合、プログラマーが使用されなくなったリソースを明示的に解放すると、パフォーマンスが向上します。明示的な制御を提供するには、IDisposableインターフェイスによって提供されるDisposeメソッドを実装します。オブジェクトのコンシューマは、オブジェクトを使用してこのメソッドを呼び出す必要があります。オブジェクトへの他の参照が生きている場合でも、Disposeを呼び出すことができます。
Disposeを使用して明示的な制御を提供する場合でも、Finalizeメソッドを使用して暗黙的なクリーンアップを提供する必要があることに注意してください。 Finalizeは、プログラマがDisposeの呼び出しに失敗した場合にリソースが永久にリークするのを防ぐためのバックアップを提供します。
C#のFinalizeメソッドとDisposeメソッドの違い。
GCはfinalizeメソッドを呼び出して、管理されていないリソース(ファイル操作、Windows API、ネットワーク接続、データベース接続など)を再利用しますが、GCが呼び出すときの時間は固定されていません。これは、GCによって暗黙的に呼び出されます。つまり、低レベルの制御はありません。
Disposeメソッド:コードから呼び出すときに、低レベルの制御を行います。管理されていないリソースを使用できないと感じるときはいつでも再利用できます。これを実現するには、IDisposalパターンを実装します。