C# 2008
私はしばらくこの作業に取り組んできましたが、コード内での finalize メソッドと dispose メソッドの使用についてまだ混乱しています。質問は次のとおりです。
アンマネージ リソースを破棄するときにのみファイナライザーが必要であることはわかっています。ただし、アンマネージ リソースを呼び出すマネージ リソースがある場合、それでもファイナライザーを実装する必要がありますか?
ただし、直接的または間接的にアンマネージ リソースを使用しないクラスを開発する場合、
IDisposable
そのクラスのクライアントが 'using ステートメント' を使用できるように を実装する必要がありますか?クラスのクライアントが using ステートメントを使用できるようにするためだけに IDisposable を実装することは可能でしょうか?
using(myClass objClass = new myClass()) { // Do stuff here }
Finalize/dispose の使用方法を示すために、以下の簡単なコードを開発しました。
public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { // Start the Async's download // Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Do work here } // Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
ソースコードに関する質問:
ここではファイナライザーを追加していませんが、通常、ファイナライザーは GC によって呼び出され、ファイナライザーは Dispose を呼び出します。ファイナライザーがないので、いつ Dispose メソッドを呼び出すのでしょうか? それを呼び出す必要があるのはクラスのクライアントですか?
したがって、この例のクラスは NoGateway と呼ばれ、クライアントは次のようにクラスを使用したり破棄したりできます。
using(NoGateway objNoGateway = new NoGateway()) { // Do stuff here }
実行が using ブロックの最後に到達すると、Dispose メソッドが自動的に呼び出されますか、それともクライアントが手動で Dispose メソッドを呼び出す必要がありますか? つまり、
NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it
私は
WebClient
自分のクラスで クラスを使用していますNoGateway
。 はインターフェイスWebClient
を実装しているのでIDisposable
、これは間接的にアンマネージ リソースを使用することを意味しますかWebClient
? これに従うための厳格なルールはありますか? クラスがアンマネージ リソースを使用していることをどのように確認すればよいですか?
ベストアンサー1
推奨されるIDisposableパターンはここIDisposable を使用するクラスをプログラミングする場合、通常は次の 2 つのパターンを使用する必要があります。
アンマネージ リソースを使用しないシール クラスを実装する場合は、通常のインターフェイス実装と同様に Dispose メソッドを実装するだけです。
public sealed class A : IDisposable
{
public void Dispose()
{
// get rid of managed resources, call Dispose on member variables...
}
}
シールされていないクラスを実装する場合は、次のようにします。
public class B : 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
}
// only if you use unmanaged resources directly in B
//~B()
//{
// Dispose(false);
//}
}
ではファイナライザーを宣言していないことに注意してください。実際に破棄するアンマネージ リソースがある場合にのみ、ファイナライザーを実装する必要があります。 が呼び出されたB
場合でも、CLR はファイナライズ可能なオブジェクトをファイナライズ不可能なオブジェクトとは異なる方法で処理します。SuppressFinalize
したがって、必要がない限りファイナライザーを宣言する必要はありませんが、クラスの継承クラスにフックを提供してDispose
、アンマネージ リソースを直接使用する場合はファイナライザー自体を実装します。
public class C : B
{
private IntPtr m_Handle;
protected override void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
ReleaseHandle(m_Handle);
base.Dispose(disposing);
}
~C() {
Dispose(false);
}
}
アンマネージ リソースを直接使用していない場合 (SafeHandle
フレンドは独自のファイナライザーを宣言するためカウントされません)、ファイナライザーを実装しないでください。後でファイナライザーを抑制したとしても、GC はファイナライズ可能なクラスを異なる方法で処理します。また、 にはB
ファイナライザーがありませんが、SuppressFinalize
ファイナライザーを実装するサブクラスを正しく処理するために を呼び出すことに注意してください。
クラスが IDisposable インターフェイスを実装する場合、クラスの使用が終了したら削除する必要があるアンマネージ リソースがどこかに存在することを意味します。実際のリソースはクラス内にカプセル化されているため、明示的に削除する必要はありません。クラスを呼び出すかDispose()
、クラスを でラップするだけでusing(...) {}
、必要に応じてアンマネージ リソースが削除されるようになります。