web-dev-qa-db-ja.com

C#Disposeメソッドの実装方法

Disposeメソッドの実装に関するアドバイスが必要です。

このアプリケーションでは、ユーザーが独自のUIを設計します。 UIがどのように見えるかを示すプレビューウィンドウがあります。このUIで描画されるすべてのオブジェクトは、最終的に共通の基本クラスScreenObjectから派生します。プレビューマネージャーには、プレビュー領域全体のグリッドオブジェクトであるScreenGridへの単一のオブジェクト参照が含まれています。

質問#1

派生したスクリーンクラスのいくつかは、データベース接続、ビットマップイメージ、WebBrowserコントロールなどのアンマネージリソースを保持します。これらのクラスは、これらのオブジェクトを破棄する必要があります。ベースDisposeベースクラスに仮想ScreenObjectメソッドを作成し、アンマネージリソースを保持する各派生クラスにオーバーライドDisposeメソッドを実装しました。ただし、今はDisposeというメソッドを作成しましたが、IDisposableを実装していません。 IDisposableを実装する必要がありますか?もしそうなら、どのように実装しますか?

  • アンマネージリソースを持つ派生クラスのみ
  • 管理されていないリソースを持つ基本クラスと派生クラス[〜#〜] or [〜#〜]
  • 管理されていないリソースを持たないものを含む、基本クラスとすべての派生クラス

多態性を利用できるように、管理されていないリソースを持たない基本クラスに仮想Disposeメソッドを配置するのは間違っていますか?

質問#2

DisposeメソッドとIDisposableインターフェイスについて読むと、Microsoftは破棄オブジェクトは親に対してDisposeメソッドのみを呼び出すべきであると述べています。親は、親などに対してそれを呼び出します。私にはこれは逆に思えます。私は子供を処分したいが、その親は維持したいかもしれません。

私はそれが反対であるべきだと思うだろう、処分されるオブジェクトはその子を処分するべきだ。その後、子供は自分の子供などを処分する必要があります。

私はここで間違っていますか、何かが欠けていますか?

31
WPFNewbie

質問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を呼び出す責任があります。

36
PVitt

質問1:

リストするオブジェクトのタイプ(データベース、WebBrowser、ビットマップなど)に基づいて、.NETに関する限り、これらはmanagedリソースのみです。 。したがって、使い捨てタイプをメンバーとして持つクラスにはIDisposableを実装する必要があります。それらがローカルで宣言されたインスタンスである場合は、それらに対して「using()」を呼び出すだけです。あなたが言及したこれらのインスタンスには、それらの下にアンマネージリソースがありますが、これは、使用している型を通して.NETによって抽象化されます。マネージ型のみを使用しているため、IDisposableを実装する必要がありますが、ファイナライザーは使用しません。クラスメンバーとしてアンマネージリソースを本当に持っている場合にのみ、ファイナライザーを実装する必要があります。

質問2:

継承と継承/集約を混同しているようです。たとえば、「コンテナ」に使い捨てのリソースがクラスメンバーとして含まれている場合、それは集約/包含と呼ばれます。したがって、コンテナの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

7
Dave Black

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;
        }
    }
}
1