5

問題は、ファイナライズが呼び出されたときにオブジェクトがリソースを破棄するという事実をどのようにテストできるかです。クラスのコード:

public class TestClass : IDisposable {

    public bool HasBeenDisposed {get; private set; }

    public void Dispose() {
        HasBeenDisposed = true;
    }

    ~TestClass() {
        Dispose();
    }
}

最初にテストする方法を見つけたいので、今は Dispose/Finalize の正しい実装は気にしないことに注意してください。この段階では、Dispose/Finalize ウェアが呼び出された場合に HasBeenDisposedが true に設定されると想定するだけで十分です。

私が書いた実際のテストは次のようになります:
UPDATED WITH WEAKREFERENCE :

[Test]
public void IsCleanedUpOnGarbadgeCollection() {
    var o = new TestClass();
    o.HasBeenDisposed.Should().Be.False();

    **var weak = new WeakReference(o, true); // true =Track after finalisation
    o = null; // Make eligible for GC**

    GC.Collect(0, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();


    **((TestClass)weak.Target)**.HasBeenDisposed.Should().Be.True();
}

または私が好きなコード(ADDED AFTER UPDATE):

[Test]
public void IsCleanedUpOnGarbadgeCollection() {
    WeakReference weak = null;

    // Use action to isolate instance and make them eligible for GC
    // Use WeakReference to track the object after finalisaiton
    Action act = () = {
        var o = new TestClass();
        o.HasBeenDisposed.Should().Be.False();
        weak = new WeakReference(o, true); // True=Track reference AFTER Finalize
    };

    act();

    GC.Collect(0, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();

    // No access to o variable here which forces us to use WeakReference only to avoid error
    ((TestClass)weak.Target).HasBeenDisposed.Should().Be.True();
}

このテストは失敗します(PASSES AFTER UPDATE)が、次のことを観察します(UPDATED):

  1. GC.WaitForPendingFinalizers() は、スレッドを中断し、インスタンスをoでファイナライズしますが、ルート化されていない場合のみです。それにNULLを割り当て、WeakReferenceを使用してファイナライズ後に取得しました。
  2. oがインスタンスを保持していない場合、ファイナライズ (デストラクタ) コードは正しいポイントで実行されます。

それで、これをテストする正しい方法は何ですか。何が恋しいですか?

GCがそれを収集できないのは変数oだと思います。
更新:はい、それは問題です。代わりに WeakReference を使用する必要がありました。

4

3 に答える 3

3

「GCがそれを収集できないのは変数oだと思います。」正しい。スタックに参照が存在するということは、オブジェクトが到達可能であることを意味し、したがってコレクション (およびファイナライズ) の対象にはなりません。

オブジェクトへの参照がなくなるまでオブジェクトはファイナライズされないため、ファイナライズ動作のテストは難しい場合があります。(オブジェクトについてアサーションを行うには、オブジェクトへの参照が必要です!) 1 つの方法は、間接的に行うことです。ファイナライズ中に、オブジェクトに何らかのメッセージを送信させます。ただし、これは純粋にテスト目的でファイナライズ コードを歪めます。オブジェクトへの弱い参照を保持することもできます。これにより、オブジェクトはファイナライズの対象になり、ファイナライザーでそれ自体を復活させることができますが、本番コードでは復活させたくありません。

于 2009-10-21T01:13:04.747 に答える
0

オブジェクトへのローカル参照がある場合、オブジェクトが収集されるのはなぜですか?

于 2009-10-21T01:12:21.680 に答える
0

メモリ プロファイラーは、リークをテストする最も適切な方法です。

.Net Memory Profiler をお勧めします。

于 2010-12-14T20:18:51.173 に答える