web-dev-qa-db-ja.com

親もIDisposableを実装する場合、サブクラスにIDisposableを実装する

両方ともIDisposableを実装する必要がある親クラスと子クラスがあります。 virtual(およびbase.Dispose()?)呼び出しはどこで機能しますか? Dispose(bool disposing)呼び出しをオーバーライドするだけで、明示的なDispose()関数を使用せずに(継承された関数を使用するだけで)IDisposableを実装すると言うのは、本当に奇妙に感じます。ほかのすべて。

私がやっていたこと(かなり些細なことです):

internal class FooBase : IDisposable
{
    Socket baseSocket;

    private void SendNormalShutdown() { }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private bool _disposed = false;
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                SendNormalShutdown();
            }
            baseSocket.Close();
        }
    }

    ~FooBase()
    {
        Dispose(false);
    }
}

internal class Foo : FooBase, IDisposable
{
    Socket extraSocket;

    private bool _disposed = false;
    protected override void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            extraSocket.Close();
        }
        base.Dispose(disposing);
    }

    ~Foo()
    {
        Dispose(false);
    }

}
25
Tanzelax

Dispose(bool disposed)呼び出しをオーバーライドするだけで、明示的なDispose()関数を持たずに(継承された関数を利用するだけで)IDisposableを実装するが、他のすべてを持っていると言うのは本当に奇妙に感じます。

これはあなたが気にするべきではないものです。

IDisposableクラスをサブクラス化すると、すべての「Disposepattern」配管が基本クラスによってすでに処理されています。実際には、protected Dispose(bool)メソッドをオーバーライドし、既に破棄されているかどうかを追跡する以外に何もする必要はありません(ObjectDisposedExceptionを適切に上げるため)。

詳細については、 IDisposableクラスからのサブクラス化 に関する私のブログ投稿を参照してください。


また、多くの場合、IDisposableクラスをサブクラス化するのではなく、カプセル化することを検討することをお勧めします。 IDisposableクラスをサブクラス化することが適切な場合もありますが、それらはややまれです。多くの場合、カプセル化がより良い代替手段です。

26
Reed Copsey

あなたがする必要がないのになぜ物事を複雑にするのですか?

管理されていないリソースをカプセル化しないので、ファイナライズをいじくり回す必要はありません。また、クラスは内部にあります。これは、独自のアセンブリ内で継承階層を制御することを示唆しています。

したがって、単純なアプローチは次のようになります。

internal class FooBase : IDisposable 
{ 
  Socket baseSocket; 

  private void SendNormalShutdown() 
  { 
    // ...
  } 

  private bool _disposed = false; 

  public virtual void Dispose() 
  { 
    if (!_disposed)
    { 
      SendNormalShutdown(); 
      baseSocket.Close(); 
      _disposed = true;
    } 
  } 
} 

internal class Foo : FooBase
{ 
  Socket extraSocket; 

  private bool _disposed = false; 

  public override void Dispose()
  { 
    if (!_disposed)
    { 
      extraSocket.Close(); 
      _disposed = true;
    } 

    base.Dispose(); 
  } 
} 

管理されていないリソースがある場合でも、 それらをカプセル化する 独自の使い捨てクラスで使用し、他の使い捨てリソースと同じように使用する方がはるかに良いと思います。上記のコードと同じくらい簡単です。

5
Jordão

このパターンの考え方は、仮想Disposeメソッドをオーバーライドし、必要に応じてbase.Disposeを呼び出すことです。基本クラスが残りを処理し、仮想Disposeメソッドを呼び出します(したがって、正しい実装)。サブクラスはIDisposableも実装する必要はありません(継承によるIDisposableです)

3
spender

私は常に、このパターンに関するJoeDuffyの非常に詳細な研究に目を向けます。私にとって、彼のバージョンは福音です。

http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/

最初に覚えておくべきことは、ファイナライザーはほとんどの場合必要ないということです。これは、ネイティブリソースを直接保持している管理されていないリソース、つまり独自のファイナライザーを持たないリソースのみをクリアするためのものです。

これは、基本クラスのサブクラスペアの例です。

// Base class

    #region IDisposable Members

    private bool _isDisposed;

    public void Dispose()
    {
        this.Dispose(true);
        // GC.SuppressFinalize(this); // Call after Dispose; only use if there is a finalizer.
    }

    protected virtual void Dispose(bool isDisposing)
    {
        if (!_isDisposed)
        {
            if (isDisposing)
            {
                // Clear down managed resources.

                if (this.Database != null)
                    this.Database.Dispose();
            }

            _isDisposed = true;
        }
    }

    #endregion


// Subclass

    #region IDisposable Members

    private bool _isDisposed;

    protected override void Dispose(bool isDisposing)
    {
        if (!_isDisposed)
        {
            if (isDisposing)
            {
                // Clear down managed resources.

                if (this.Resource != null)
                    this.Resource.Dispose();
            }

            _isDisposed = true;
        }

        base.Dispose(isDisposing);
    }

    #endregion

サブクラスには独自の_isDisposedメンバーがあることに注意してください。これらのブロックで例外を発生させたくないので、リソースのnullチェックにも注意してください。

ルカ

1
Luke Puplett

子クラスは、仮想Disposeをオーバーライドし、サブクラスに固有の破棄を実行し、スーパークラスのDisposeを呼び出す必要があります。これにより、独自の作業が実行されます。

編集: http://davybrion.com/blog/2008/06/disposed-of-the-idisposable-implementation/ は、そのような場合に従うパターンです。特に「Disposable」クラスではなく、継承とオーバーライド。

1
Grant Palin