質問

次のように IDisposable を実装する抽象クラスがあります。

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}

Visual Studio 2008 Team System でプロジェクトに対してコード分析を実行したところ、次のような警告が表示されました。

Microsoft.デザイン:Dispose(true) を呼び出してから、現在のオブジェクト インスタンス (Visual Basic の 'this' または 'Me') で GC.SuppressFinalize を呼び出して戻るように、'ConnectionAccessor.Dispose()' を変更します。

抽象メソッドの本体を変更するように指示するのはただの愚かなことでしょうか、それとも派生インスタンスでさらに何かを行う必要がありますか? Dispose?

役に立ちましたか?

解決

あなたはDisposeを実装するための従来のパターンに従ってください。従来のパターンは、「管理対象のクリーンアップ」(APIクライアント呼び出しDispose()直接またはDispose()経由)と「管理対象外のクリーンアップ」(GC呼び出すファイナライザ)内のコードの再利用を重視しているため、悪い習慣と考えられているusingが仮想作ります。思い出させるために、パターンはこれです:

public class Base
{
    ~Base()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // so that Dispose(false) isn't called later
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
             // Dispose all owned managed objects
        }

        // Release unmanaged resources
    }
}

ここで重要なのは、そこに管理されていないクリーンアップのためのファイナライザとDisposeの間には重複がいない、とまだ派生クラスは、両方の管理とアンマネージクリーンアップを拡張することができるということです。

あなたのケースのために、何をやるべきことはこれです:

protected abstract void Dispose(bool disposing)

とあるとして他のすべてを残します。そしてどのようにあなたがそれらのすべてがそれを必要とすることを知っています - あなたが今Disposeを実装するために、あなたの派生クラスを強制しているので、それさえもが、怪しげな価値がありますか?お使いのベースクラスを処分することは何もありませんが、ほとんどの派生クラスの可能性が高い(おそらく、いくつかの例外を除いて)を行う場合には、単に空の実装を提供します。それはSystem.IO.Stream(それ自体抽象的には)何をするかですので、先例があります。

他のヒント

警告は基本的にhref="http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx" rel="noreferrer">の廃棄パターンあなたのクラスでするます。

結果のコードは次のようになります:

public abstract class ConnectionAccessor : IDisposable
{
    ~ConnectionAccessor()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
    }
}

これまでに提供された回答について私が唯一不満に思うのは、それらがすべてあなたが次のように仮定していることです。 必要 ファイナライザが必要ですが、必ずしもそうであるとは限りません。ファイナライゼーションに関連してかなり大きなパフォーマンスのオーバーヘッドが発生します。必要がない場合は、すべての派生クラスにこれを課したくありません。

見る このブログ投稿 Joe Duffy 著。ファイナライザーが必要な場合と不要な場合、およびどちらの場合でも Dispose パターンを適切に実装する方法について説明しています。
Joe のブログ投稿を要約すると、アンマネージ メモリを扱うかなり低レベルの作業を行っていない限り、ファイナライザーを実装すべきではありません。一般的な経験則として、クラスが IDisposable 自体を実装するマネージ型への参照のみを保持する場合、ファイナライザーは必要ありません (ただし、IDisposable を実装し、それらのリソースを破棄する必要があります)。アンマネージ リソースをコード (PInvoke?) から直接割り当てており、それらのリソースを解放する必要がある場合は、リソースが必要です。本当に必要な場合、派生クラスはいつでもファイナライザーを追加できますが、すべての派生クラスにファイナライザーを基本クラスに含めることで強制的にファイナライザーを持たせると、オーバーヘッドが発生しない可能性がある場合でも、すべての派生クラスがファイナライズ可能なオブジェクトのパフォーマンス ヒットの影響を受けることになります。必要。

それは少しNIT-ピッキングのように見えるんが、

、アドバイスは有効です。あなたはすでにあなたがConnectionAccessorのいずれかのサブタイプがあります期待していることを示すされているの何かの彼らは処分する必要があります。したがって、適切なクリーンアップは、基本クラスではなく、それを行うには、各サブタイプに依存する(GC.SuppressFinalizeコールの面で)行われていることを確認した方が良いようです。

私はブルース・ワーグナーブック効果的なC#のの中で言及した処分パターンを使用します基本的には、次のとおりです。

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }

警告はしかし興味深いです。エリックリッペルト、C#のデザイナーの一つは、エラーメッセージがあるべき理由についてブログ「の診断が、規範的ではない:問題ではなく、解決策を説明し」。 ここで読みます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top