次のコードを検討してください。
_namespace DisposeTest
{
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Calling Test");
Test();
Console.WriteLine("Call to Test done");
}
static void Test()
{
DisposeImplementation di = new DisposeImplementation();
}
}
internal class DisposeImplementation : IDisposable
{
~DisposeImplementation()
{
Console.WriteLine("~ in DisposeImplementation instance called");
}
public void Dispose()
{
Console.WriteLine("Dispose in DisposeImplementation instance called");
}
}
}
_
たとえTest();
呼び出しの後に待機ループを置いても、Disposeが呼び出されることはありません。つまり、それはかなり悪いことです。考えられるすべてのリソースが確実にクリーンアップされるように、簡単で非常に使いやすいクラスを作成したいと考えています。クラスのユーザーにその責任を負わせたくありません。
可能な解決策:using
を使用するか、Dispose myselfを呼び出します(基本的に同じです)。ユーザーに使用を強制することはできますか?または、破棄を強制的に呼び出すことはできますか?
GC.Collect();
の後にTest();
を呼び出しても機能しません。
di
をnull
に入れても、Disposeは呼び出されません。 Deconstructorは機能するので、オブジェクトは終了時に分解されますTest()
わかりました、今ははっきりしています!
ご回答ありがとうございます!コメントに警告を追加します!
考えられるすべてのリソースが確実にクリーンアップされるように、簡単で非常に使いやすいクラスを作成したいと考えています。クラスのユーザーにその責任を負わせたくありません。
それはできません。メモリ管理は、特にメモリではないリソースに対応するように構築されていないだけです。
IDisposableパターンは、参照カウントなどを使用してメモリ管理がそれを理解しようとするのではなく、オブジェクトが使用されたときにオブジェクトを通知する方法として開発者を対象としています。
Finalizerは、オブジェクトを適切に破棄できないユーザーのフォールバックとして使用できますが、オブジェクトをクリーンアップするための主要な方法としてはうまく機能しません。円滑に作業するには、オブジェクトを適切に配置する必要があります。これにより、よりコストのかかるFinalizerを呼び出す必要がなくなります。
OPの質問に対処するには、いくつかの重要なポイントを作成する必要があります。
using()
ブロックでラップしてください。Finalizer:一部の開発者はこれをデストラクタと呼んでいます。 そして実際、それは C#4.0言語仕様(セクション1.6.7.6) およびprevious現在のバージョン ECMA-334仕様。幸い、第4版(2006年6月)では、セクション8.7.9でファイナライザを正しく定義し、セクション17.12で2つの間の混乱を解消しようとしています。 .NET Frameworkの従来のデストラクターとデストラクター/ファイナライザーとして知られているものの間には、重要な内部の違い(ここでこれらの細かい詳細に入る必要はありません)があることに注意してください。
GC.SuppressFinalize()
が呼び出されない場合にのみ、.NET Frameworkによってファイナライザが呼び出されます。GC.Collect(2)
を呼び出すことで強制できます。ファイナライズ:ファイナライズは、「正常な」クリーンアップとリソースの解放を処理する.NET Frameworkの方法です。
これは確かにあなたが求めたより多くの情報ですが、それは物事がどのように機能するか、なぜ彼らが彼らのやり方で機能するのかについての背景を提供します。一部の人々は、.NETでのメモリとリソースの管理について心配する必要はないと主張しますが、それによって、実行する必要があるという事実が変わることはありません。また、近い将来にそうなるとは思わないでしょう。
すべての答えは(多かれ少なかれ)正しいです、ここに例があります:
_static void Test()
{
using (DisposeImplementation di = new DisposeImplementation())
{
// Do stuff with di
}
}
_
Dispose
を手動で呼び出すこともできますが、using
ステートメントの利点は、例外がスローされるため、制御ブロックを離れるとオブジェクトも破棄されることです。
IDisposableインターフェイスの使用を「忘れた」場合に備えて、破棄するリソースを処理するファイナライザを追加できます。
_public class DisposeImplementation : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
// get rid of unmanaged resources
}
~DisposeImplementation()
{
Dispose(false);
}
}
_
詳細は この質問 を参照してください。ただし、これはクラスを正しく使用していない人々を補うためのものです:)開発者に間違いを警告するために、Finalizerに大きなfat Debug.Fail()
呼び出しを追加することをお勧めします。
パターンの実装を選択すると、GC.Collect()
が破棄をトリガーすることがわかります。
これをクラスのパターン/テンプレートとして使用します
_public class MyClass : IDisposable
{
private bool disposed = false;
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
......
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
...........................
// Note disposing has been done.
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyClass()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
_
そしてもちろん、他の人が述べたようにusing(...){}
ブロックを忘れないでください。
Dispose
を明示的に呼び出すか、オブジェクトをusing
ステートメントでラップする必要があります。例:
using (var di = new DisposeImplementation())
{
}
可能な解決策:usingを使用するか、Dispose myselfを呼び出します(基本的に同じ)。
using
を使用することは、Dispose
ブロック内でfinally
を呼び出すことと同じです。
Dispose
メソッドを呼び出すか、using
を使用して、自分で破棄することになっています。覚えておいてください、それはデコンストラクタではありません!
クラスのユーザーがリソースを適切に破棄することを信頼できない場合、おそらくユーザーは他の方法で混乱するでしょう。
Disposeは自動的に呼び出されません。 using
句を使用して使用法をラップするか、手動で呼び出す必要があります。
参照 http://msdn.Microsoft.com/en-us/library/aa664736%28VS.71%29.aspx
デストラクタからdispose
を呼び出せない...プロジェクトでこれを少し前に試しました。