Dispose
メソッドの実装に関するアドバイスが必要です。
このアプリケーションでは、ユーザーが独自のUIを設計します。 UIがどのように見えるかを示すプレビューウィンドウがあります。このUIで描画されるすべてのオブジェクトは、最終的に共通の基本クラスScreenObjectから派生します。プレビューマネージャーには、プレビュー領域全体のグリッドオブジェクトであるScreenGridへの単一のオブジェクト参照が含まれています。
質問#1
派生したスクリーンクラスのいくつかは、データベース接続、ビットマップイメージ、WebBrowser
コントロールなどのアンマネージリソースを保持します。これらのクラスは、これらのオブジェクトを破棄する必要があります。ベースDispose
ベースクラスに仮想ScreenObject
メソッドを作成し、アンマネージリソースを保持する各派生クラスにオーバーライドDispose
メソッドを実装しました。ただし、今はDispose
というメソッドを作成しましたが、IDisposable
を実装していません。 IDisposable
を実装する必要がありますか?もしそうなら、どのように実装しますか?
多態性を利用できるように、管理されていないリソースを持たない基本クラスに仮想Dispose
メソッドを配置するのは間違っていますか?
質問#2
Dispose
メソッドとIDisposable
インターフェイスについて読むと、Microsoftは破棄オブジェクトは親に対してDispose
メソッドのみを呼び出すべきであると述べています。親は、親などに対してそれを呼び出します。私にはこれは逆に思えます。私は子供を処分したいが、その親は維持したいかもしれません。
私はそれが反対であるべきだと思うだろう、処分されるオブジェクトはその子を処分するべきだ。その後、子供は自分の子供などを処分する必要があります。
私はここで間違っていますか、何かが欠けていますか?
質問1:次のパターンを使用して、IDisposable
も実装します。
public class MyClass : IDisposable
{
bool disposed;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//dispose managed resources
}
}
//dispose unmanaged resources
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
質問2:マイクロソフトが意味するのは、派生クラスが親クラスでdisposeを呼び出すことです。インスタンスの所有者は、最も派生した型でのみDisposeを呼び出します。
(短縮)の例:
class Parent : IDisposable
{
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//dispose managed resources
}
}
//dispose unmanaged resources
disposed = true;
}
}
class Child : Parent, IDisposable
{
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//dispose managed resources
}
base.Dispose(disposing);
}
//dispose unmanaged resources
disposed = true;
}
}
class Owner:IDisposable
{
Child child = new Child();
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if(child!=null)
{
child.Dispose();
}
}
}
//dispose unmanaged ressources
disposed = true;
}
}
所有者は、子に対してDispose
のみを呼び出し、親に対しては呼び出しません。子は、親でDispose
を呼び出す責任があります。
リストするオブジェクトのタイプ(データベース、WebBrowser、ビットマップなど)に基づいて、.NETに関する限り、これらはmanagedリソースのみです。 。したがって、使い捨てタイプをメンバーとして持つクラスにはIDisposable
を実装する必要があります。それらがローカルで宣言されたインスタンスである場合は、それらに対して「using()」を呼び出すだけです。あなたが言及したこれらのインスタンスには、それらの下にアンマネージリソースがありますが、これは、使用している型を通して.NETによって抽象化されます。マネージ型のみを使用しているため、IDisposable
を実装する必要がありますが、ファイナライザーは使用しません。クラスメンバーとしてアンマネージリソースを本当に持っている場合にのみ、ファイナライザーを実装する必要があります。
継承と継承/集約を混同しているようです。たとえば、「コンテナ」に使い捨てのリソースがクラスメンバーとして含まれている場合、それは集約/包含と呼ばれます。したがって、コンテナのIDisposable
実装でbase.Dispose()
を呼び出すことは、コンテナの使い捨てリソースinsideの破棄とは関係ありません。クラスが「DerivedContainer」などのコンテナから派生する場合、追加のメンバーや機能を備えているにもかかわらず、コンテナのインスタンスであることを忘れないでください。したがって、「DerivedContainer」のインスタンスには、その基本クラス「Container」が持つすべてのメンバーが含まれます。 base.Dispose()
を一度も呼び出さなかった場合、「Container」の使い捨てリソースは適切に解放されません(実際にはGCによって解放されますが、多くの理由で「.NETが処理する」のは悪い習慣です) )-。NETの自動ガベージコレクターに依存するのは悪い習慣ですか?に投稿された回答を参照してください。
基本クラスDispose()
を呼び出さなかった場合、部分的に破棄されたオブジェクト(派生クラスに配置されますが、基本クラスには配置されません)になります-非常に悪いシナリオです。そのため、基本クラスDispose()
を呼び出すことが非常に重要です。
私が開発したベストプラクティスパターン(多くの経験とメモリダンプのデバッグ)が私のブログに例として書かれています。基本クラスと派生クラスにIDisposable
を実装する方法を示します。
http://dave-black.blogspot.com/2011/03/how-do-you-properly-implement.html
IDisposableを実装します
class ConnectionConfiguration:IDisposable
{
private static volatile IConnection _rbMqconnection;
private static readonly object ConnectionLock = new object();
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
if (_rbMqconnection == null)
{
return;
}
lock (ConnectionLock)
{
if (_rbMqconnection == null)
{
return;
}
_rbMqconnection?.Dispose();//double check
_rbMqconnection = null;
}
}
}