回答:
他の人はDispose
との違いをすでにカバーしていますFinalize
(ところで、Finalize
メソッドは言語仕様でデストラクタと呼ばれています)。したがって、Finalize
メソッドが役立つシナリオについて少しだけ追加します。
一部のタイプでは、使い捨てリソースを使いやすく、1回のアクションで簡単に破棄できる方法でカプセル化しています。一般的な使用法は、多くの場合、次のようになります。オープン、読み取りまたは書き込み、クローズ(破棄)。それはusing
構造と非常によく適合します。
その他はもう少し難しいです。WaitEventHandles
たとえば、あるスレッドから別のスレッドに信号を送るために使用されるため、インスタンスはこのように使用されません。次に、誰がDispose
これらを呼び出す必要があるのでしょうか?これらのような保護手段として、Finalize
インスタンスがアプリケーションによって参照されなくなったときにリソースが破棄されるようにするメソッドが実装されています。
Finalize
正当化される可能性のある最大の状況は、リソースの存続に関心のあるオブジェクトが多数ある場合ですが、リソースに関心がなくなったオブジェクトが、それが最後の一つ。そのような場合、Finalize
通常、誰もオブジェクトに興味がないときにのみ起動します。の緩やかなタイミングはFinalize
、ファイルやロックなどのファンジブルではないリソースでは恐ろしいですが、ファンジブルなリソースでは問題ない場合があります。
finalizerメソッドは、オブジェクトがガベージコレクションされたときに呼び出され、これがいつ発生するかは保証されません(強制することはできますが、パフォーマンスが低下します)。
Dispose
一方、このメソッドはクラスを作成したコードによって呼び出されるため、コードが実行された瞬間に、取得したリソース(アンマネージデータ、データベース接続、ファイルハンドルなど)をクリーンアップして解放できます。あなたのオブジェクト。
標準的な方法は実装することでIDisposable
ありDispose
、そのため、オブジェクトをusing
ステートメントで使用できます。などusing(var foo = new MyObject()) { }
。そしてファイナライザではDispose
、呼び出しコードがあなたを破棄するのを忘れた場合に備えて、を呼び出します。
Finalizeは、オブジェクトを再生するときにガベージコレクターによって呼び出されるバックストップメソッドです。Disposeは「確定的なクリーンアップ」メソッドであり、アプリケーションによって呼び出され、貴重なネイティブリソース(ウィンドウハンドル、データベース接続など)が不要になったときに解放されます。
オブジェクトのユーザーとして、常にDisposeを使用します。FinalizeはGC用です。
クラスの実装者として、破棄する必要があるマネージリソースを保持している場合は、Disposeを実装します。ネイティブリソースを保持している場合は、DisposeとFinalizeの両方を実装し、どちらもネイティブリソースを解放する共通のメソッドを呼び出します。これらのイディオムは、通常、Disposeをtrueで呼び出し、Finalize呼び出しをfalseで呼び出すプライベートDispose(bool disposing)メソッドを介して結合されます。このメソッドは常にネイティブリソースを解放してから、破棄パラメーターをチェックし、trueの場合はマネージリソースを破棄してGC.SuppressFinalizeを呼び出します。
Dispose
良いですし、正しく実装するのは一般的に簡単です。 Finalize
悪であり、それを正しく実装することは一般に困難です。とりわけ、GCは、そのオブジェクトへの参照が存在する限り、オブジェクトのIDが「リサイクル」されないことを保証するため、Disposable
既にクリーンアップされている可能性があるオブジェクトの束をクリーンアップするのは簡単です。問題ない; オブジェクトへの参照は、どのDispose
すでに呼び出されたときにオブジェクトへの参照残るDispose
既に呼ばれています。
Fred
がファイルハンドル#42を所有して閉じている場合、システムは同じ番号をいくつかの他のエンティティに与えられているファイルハンドルに添付する可能性があります。その場合、ファイルハンドル#42はFredの閉じたファイルではなく、他のエンティティによってアクティブに使用されていたファイルを参照します。以下のためにFred
近くのハンドル#42にしようとする再び悲惨なことでしょう。1つの管理されていないオブジェクトがまだ解放されているかどうかを100%確実に追跡することは、実行可能です。複数のオブジェクトを追跡することは非常に困難です。
ファイナライズ
protected
でないpublic
場合private
は、アプリケーションのコードからメソッドを直接呼び出すことはできません。同時に、base.Finalize
メソッドを呼び出すことができます。廃棄
IDisposable
ファイナライザを持つすべての型に実装するDispose
メソッドを呼び出した後、オブジェクトが使用不可になっていることを確認してください。つまり、Dispose
メソッドが呼び出された後のオブジェクトの使用は避けてください。Dispose
すべてのIDisposable
タイプを呼び出すDispose
エラーを上げることなく、複数回呼び出されます。Dispose
メソッドを使用して、GC.SuppressFinalize
メソッド内からファイナライザへの以降の呼び出しを抑制しますDispose
メソッド内からの例外のスローを回避する廃棄/最終パターン
Dispose
およびFinalize
アンマネージリソースを使用する場合に実装することをお勧めします。Finalize
実装が実行されますと、オブジェクトがガベージコレクトされている場合、リソースはまだ開発者が呼び出しすることを怠った場合でもリリースされるDispose
明示的方法を。Finalize
メソッドとメソッドのアンマネージリソースをクリーンアップしDispose
ます。さらに、Dispose
そのクラス内のコンポーネントとして(管理されていないリソースをメンバーとして持つ).NETオブジェクトのメソッドをメソッドから呼び出しますDispose
。Finalizeは、このオブジェクトが使用されなくなったときにGCによって呼び出されます。
Disposeは、このクラスのユーザーがリソースを解放するために呼び出すことができる通常のメソッドです。
ユーザーがDisposeの呼び出しを忘れた場合、およびクラスにFinalizeが実装されている場合、GCはそれが確実に呼び出されるようにします。
本MCSD Certification Toolkit(exam 70-483)pag 193から、いくつかのキーがあります。
デストラクタ≈(ほぼ等しい)base.Finalize()
、デストラクタは、デストラクタのコードを実行し、基本クラスのFinalizeメソッドを呼び出すFinalizeメソッドのオーバーライドバージョンに変換されます。その後、GCに依存しているため、いつ呼び出されるかを知ることはできません。
クラスにマネージリソースとアンマネージリソースが含まれていない場合、クラスはIDisposable
デストラクタを実装または持つべきではありません。
クラスにマネージリソースのみがある場合、クラスは実装IDisposable
する必要がありますが、デストラクタを持つべきではありません。(デストラクタが実行されると、管理対象オブジェクトがまだ存在するかどうか確信が持てないため、それらのDispose()
メソッドを呼び出すことはできません。)
クラスにアンマネージリソースのみIDisposable
がある場合は、プログラムを呼び出さない場合に備えて、クラスを実装してデストラクタを必要としますDispose()
。
Dispose()
メソッドは、複数回実行しても安全でなければなりません。これは、変数を使用して、それが以前に実行されたことがあるかどうかを追跡することで実現できます。
Dispose()
マネージリソースとアンマネージリソースの両方を解放する必要があります。
デストラクタは、アンマネージリソースのみを解放する必要があります。デストラクタが実行されると、管理対象オブジェクトがまだ存在するかどうか確信が持てないため、いずれにしてもDisposeメソッドを呼び出すことはできません。これはprotected void Dispose(bool disposing)
、管理されたリソースのみが解放される(破棄される)正規パターンを使用して取得されdisposing == true
ます。
リソースを解放した後、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);
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
// 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;
}
}
}
99%の確率で、どちらも心配する必要はありません。:)しかし、オブジェクトが非管理対象リソース(ウィンドウハンドル、ファイルハンドルなど)への参照を保持している場合は、管理対象オブジェクトがそれらのリソースを解放する方法を提供する必要があります。Finalizeは、リソースの解放を暗黙的に制御します。ガベージコレクタによって呼び出されます。Disposeは、リソースのリリースを明示的に制御する方法であり、直接呼び出すことができます。
ガベージコレクションのテーマについて学ぶべきことは他にもたくさんありますが、それは始まりです。
ファイナライザは暗黙的なクリーンアップのためのものです。絶対にクリーンアップする必要のあるリソースをクラスが管理する場合は、これを使用する必要があります。そうしないと、ハンドルやメモリなどがリークするためです。
ファイナライザを正しく実装することは非常に困難であり、可能な限り回避する必要があります。SafeHandle
クラス(.Net v2.0以降で使用可能)は、ファイナライザを実装する必要がほとんどないことを意味します。
IDisposable
インターフェイスは、明示的なクリーンアップのためのものであり、はるかに一般的に使用されている-あなたは、彼らがオブジェクトを使い終わったときに、ユーザーが明示的にリソースを解放またはクリーンアップできるようにするために、これを使用する必要があります。
ファイナライザIDisposable
がある場合、オブジェクトがガベージコレクションされた場合よりも早くユーザーがそれらのリソースを明示的に解放できるようにインターフェイスも実装する必要があることに注意してください。
ファイナライザとに関する推奨事項の最良かつ最も完全なセットであると私が考えるものについては、DG Update:Dispose、Finalization、およびResource Managementを参照してくださいIDisposable
。
要約は-
また、もう1つの違いは、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の呼び出しに失敗した場合にリソースが永続的にリークするのを防ぐためのバックアップを提供します。
DisposeとFinalizeの主な違いは次のとおりです。
Dispose
通常、コードによって呼び出されます。リソースは、呼び出すとすぐに解放されます。人々はメソッドの呼び出しを忘れているので、using() {}
ステートメントが発明されました。プログラムが内のコードの実行を完了すると、自動的にmethod {}
が呼び出されDispose
ます。
Finalize
コードによって呼び出されません。ガベージコレクター(GC)によって呼び出されることを意味します。つまり、GCがそうすることを決定したときはいつでも、リソースは将来いつでも解放される可能性があります。GCが機能するとき、GCは多くのFinalizeメソッドを通過します。これに重いロジックがあると、プロセスが遅くなります。プログラムのパフォーマンスの問題が発生する可能性があります。そこに何を入れるか注意してください。
個人的には、破棄ロジックのほとんどをDisposeで記述します。うまくいけば、これで混乱が解消されます。
私たちが知っているように、disposeとfinalizeは両方とも管理されていないリソースを解放するために使用されます。
最初の部分に答えるには、まったく同じクラスオブジェクトに対して人々が異なるアプローチを使用する例を提供する必要があります。そうでなければ、答えるのは難しい(または奇妙なことですらある)。
2番目の質問については、より良い最初に、この読み IDisposableインターフェイスを適切に使用し ている主張
それはあなたの選択です!しかし、Disposeを選択します。
つまり、GCはファイナライザ(存在する場合)のみを認識します。Microsoftのデストラクタとしても知られています。優れたコードは、両方(ファイナライザとDispose)からクリーンアップを試みます。