2

私はそれへの参照を見つけることができませんが、デストラクタ内または IDisposable の Dispose() メソッド内で仮想 (ポリモーフィック) メソッドを呼び出すことは良い考えではないことを読んだことを覚えています。

これは本当ですか?もしそうなら、誰かが理由を説明できますか?

4

4 に答える 4

5

コンストラクターで行うDisposeのと同じ理由で、 finalizer/ からの仮想メソッドの呼び出しは安全ではありません。派生クラスが、仮想メソッドが適切に実行するために必要とするいくつかの状態をまだクリーンアップしていないことを確認することは不可能です。

一部の人々は、標準の Disposable パターンとその仮想メソッドの使用に混乱しており、これにより、破棄中に任意のvirtual Dispose(bool disposing)仮想メソッドを使用しても問題ないと考えています。次のコードを検討してください。

class C : IDisposable {
    private IDisposable.Dispose() {
        this.Dispose(true);
    }
    protected virtual Dispose(bool disposing) {
        this.DoSomething();
    }

    protected virtual void DoSomething() {  }
}
class D : C {
    IDisposable X;

    protected override Dispose(bool disposing) {
        X.Dispose();
        base.Dispose(disposing);
    }

    protected override void DoSomething() {
        X.Whatever();
    }
}

Dと呼ばれるタイプのオブジェクトを Dispose すると、次のようになりますd

  1. いくつかのコード呼び出し((IDisposable)d).Dispose()
  2. C.IDisposable.Dispose()仮想メソッドを呼び出しますD.Dispose(bool)
  3. D.Dispose(bool)処分しますD.X
  4. D.Dispose(bool)C.Dispose(bool) 静的に呼び出します(呼び出しのターゲットはコンパイル時に認識されます)
  5. C.Dispose(bool)仮想メソッドを呼び出しますD.DoSomething()
  6. D.DoSomethingD.X.Whatever()すでに破棄されたメソッドを呼び出しますD.X
  7. ?

さて、このコードを実行するほとんどの人は、それを修正するために 1 つのことを行いbase.Dispose(dispose)ます。自分のオブジェクトをクリーンアップする前に、呼び出しを に移動します。そして、はい、それはうまくいきます。しかし、あなたが開発した会社のウルトラジュニア開発者であり、Cwrite を割り当てられたProgrammer XDが、エラーが検出されるように、またはbase.Dispose(disposing)適切な場所で呼び出しが行われるようにそれを作成することを本当に信頼していますか?

Dispose から仮想メソッドを呼び出すコードを絶対に書くべきではないと言っているのではなく、下に派生したクラスで定義されている状態を決して使用しないという仮想メソッドの要件を文書化する必要があるというだけです。C

于 2008-09-26T05:46:42.420 に答える
1

仮想メソッドの呼び出しに対する推奨事項はないと思います。覚えている禁止事項は、ファイナライザーで管理対象オブジェクトを参照することに対する規則である可能性があります。

Dispose() の実装方法に関する .Net ドキュメントで定義されている標準パターンがあります。パターンは非常にうまく設計されており、厳密に従う必要があります。

要点は次のとおりです。Dispose() は、仮想メソッド Dispose(bool) を呼び出す非仮想メソッドです。boolean パラメーターは、メソッドが Dispose() (true) またはオブジェクトのデストラクタ (false) から呼び出されているかどうかを示します。継承の各レベルで、クリーンアップを処理するために Dispose(bool) メソッドを実装する必要があります。

Dispose(bool) に値 false が渡されると、ファイナライザーが dispose メソッドを呼び出したことを示します。この状況では、管理されていないオブジェクトのクリーンアップのみを試行する必要があります (特定のまれな状況を除く)。これは、ガベージ コレクターが finalize メソッドを呼び出したばかりであるためです。したがって、現在のオブジェクトはファイナライズの準備ができているとマークされている必要があります。したがって、それが参照する任意のオブジェクトも、ファイナライズのために読み取り可能とマークされている可能性があり、シーケンスが非決定論的であるため、ファイナライズがすでに発生している可能性があります。

.Net ドキュメントで Dispose() パターンを調べて正確に従うことを強くお勧めします。奇妙で難しいバグから保護される可能性が高いからです。

于 2008-09-26T05:20:17.037 に答える
1

仮想メソッドは、コンストラクタとデストラクタの両方で推奨されません。

その理由は何よりも実用的です。仮想メソッドは、オーバーライド者によって選択された任意の方法でオーバーライドできます。たとえば、構築中のオブジェクトの初期化などは、ランダムな null と無効なオブジェクトが作成されないようにする必要があります州。

于 2008-09-26T03:41:01.287 に答える
0

ジョンの答えを拡張するには、そのレベルでリソースを処理する必要がある場合は、仮想メソッドを呼び出す代わりに、サブクラスのディスポーズまたはデストラクタをオーバーライドする必要があります。

ただし、ここでの行動に関して「ルール」があるとは思いません。しかし、一般的な考え方は、リソースのクリーンアップをその実装レベルのそのインスタンスだけに分離したいということです。

于 2008-09-26T03:53:49.757 に答える