4

私はメモリ管理について読んでいて、本もグーグルも正確な答えを考え出していないプロジェクトの状況に出くわしました。デリゲートがオブジェクトを管理し、イベントがデリゲートインスタンスであることはすでに知っています。そうは言っても、アプリケーションが終了すると、デリゲートインスタンスはメモリから削除されます。

私が理解できないのは、クラスが破棄されるまでに(明示的にまたはGCによって)外部コードがすべてのイベント参照を解放したことを確認する方法です。例として、クラスAはイベントを公開し、クラスBはそれを消費します。クラス呼び出しデリゲートへの参照を解放せずBにクラスでDisposeします。Aもちろん、Disposeメソッド自体からエラーをスローすることはできません。

以下は、デリゲートとそれを消費する別のクラスを持つクラスです。

public class ClassB
{
    private ClassA A { get; set; }

    public ClassB()
    {
        this.A = new ClassA();
        this.A.OnProcessed += new ClassA.DelegateProcessed(this.ClassA_Processed);
    }

    public void Process()
    {
        this.A.Process();
    }

    public void ClassA_Processed (ClassA sender, EventArgs e)
    {
        // Do something.

        // Code written by another developer does not free up events before calling Dispose.

        this.A.Dispose();
        this.A = null;
    }
}

public class ClassA: IDisposable
{
    public delegate void DelegateProcessed (A sender, EventArgs e);
    public event DelegateProcessed OnProcessed = null;

    ~ClassA() { this.Dispose(false); }

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

    private void Dispose (bool disposing)
    {
        if (!this.Disposed)
        {
            if (disposing)
            {
                // Dispose managed resources here.
                // Is it possible / advisable to dispose of delegates / events here?
                // Will this adversely affect the consumer class?
                this.OnProcessed -= new ClassA.DelegateProcessed(this.ClassA_Processed);
            }
        }
        this.Disposed = true;
    }

    public void Process () { this.OnProcessed(this, new EventArgs()); }

    public void ClassA_Processed (ClassA sender, EventArgs e) { }
}

重要なのは、開発者がClassBで何をするかに関係なく、ClassAがガベージコレクションの対象となるようにすることです。重要なのは、消費者が不注意であっても、ClassAがメモリに費やす時間を最小限に抑えることです。

更新:回答から、イベントをClassAから明示的に削除する必要がないことは明らかです。主な質問については、弱参照が以下のように進む方法のようです。目的は、ClassAがメモリに留まる時間を最小限に抑えることです。何か見落とした場合に備えてお知らせください。

4

4 に答える 4

2

IDisposable管理されていないリソースを決定論的に解放するために使用されます。

イベントハンドラーを削除する必要はありません。たとえば、WindowsフォームFormUserControlクラス、またはASP.NETPageUserControlクラスを見ると、これらはすべて、IDisposableイベントが広範囲に使用されており、破棄中に特別な処理が行われることはありません。

于 2012-09-24T14:04:06.933 に答える
1

コードのこのセクション:

private ClassA A { get; set; }

public ClassB()
{
    this.A = new ClassA();
    this.A.OnProcessed += new ClassA.DelegateProcessed(this.ClassA_Processed);
}

何もする必要がないことを意味します。

インスタンスはBインスタンスを所有し、は(イベントを介して)再びへの参照を持ちAます。AB

aBが到達不能になると、Aも収集されます(GCおよび循環参照)。

「A」が(長い)前に配置さBれると、「A」も収集されます(方向性)。

上のIDispoableインターフェースAは無意味です。


そして、実装に関して:

 // class B
   this.A.OnProcessed += new ClassA.DelegateProcessed(this.ClassA_Processed);

 // in classA
   this.OnProcessed -= new ClassA.DelegateProcessed(this.ClassA_Processed);

これは機能しません。2つの異なる方法は、this2つの異なる方法であることを意味します。

于 2012-09-24T14:04:39.683 に答える
1

「従来の」イベント サブスクリプションの代わりに、Weak Event Patternsを確認する必要があります。

イベント サブスクリプションは、これらの参照が残っている唯一の参照であり、参照されたオブジェクト自体が既に範囲外になっている場合でも、オブジェクトを存続させることができます。この場合、参照されたオブジェクトは GarbageCollector によって収集されることはなく、アプリケーションが終了するまで存続します。

これにより、深刻なメモリ リークが発生します。

Weak Events パターンを使用している場合、GabageCollector は、オブジェクトがまだ参照されているかどうか、またはイベントが唯一の参照であるかどうかをより適切に判断できます。この場合、オブジェクトが収集され、リソースが解放されます。

于 2012-09-24T14:06:04.093 に答える
0

適切に記述されたクラスは、そのIDisposable.Disposeメソッドで、サブスクライブしたイベントのサブスクライブを解除する必要があります。イベントがサブスクライブされたオブジェクトのGCライフタイムが、サブスクライブしたオブジェクトの有効期間に匹敵する場合(これは非常に一般的なケースです)、サブスクリプションがクリーンアップされたか、ぶら下がったままであるかは関係ありません。残念ながら、のイベントAからのサブスクライブを解除せずに放棄され、何かが(意図的かどうかにかかわらず)長期間の参照を保持する場合、存続するものはすべて存続し、直接または間接の参照を保持するもの(オブジェクトを含む)も存続します。からアクティブなイベントサブスクリプションを持っているBBBAAA)。通常はガベージコレクションの対象となる相互接続されたオブジェクトの大きなフォレストになってしまうのは非常に簡単ですが、それらのいずれかが必要な限り、すべてを存続させる必要があります。

それはあまりにも悪いイベントサブスクリプションであり、サブスクリプション解除はとても厄介です。イベントに関連付けられたオブジェクトタイプがある場合、さまざまなイベントをサブスクライブしようとしているオブジェクトは、「イベントマネージャー」オブジェクトを使用してサブスクリプションを管理できます(つまり、次のようMyEventManager.Subscribe(SomeObject.SomeEvent, someProc)に言ってMyEventManager.Dispose、すべてのイベントのサブスクライブを解除できます。確立されたサブスクリプション残念ながら、メソッドにイベントをパラメーターとして受け入れさせる適切な方法はないため、着信サブスクリプションを管理するための汎用クラスを用意する方法はありませんCleanupManager。デリゲートのペアであり、 `MyCleaner.Register(()=> {SomeObject.SomeEvent + = someProc;}、()=> {SomeObject.SomeEvent-= someProc();})のように呼び出されますが、それはかなり厄介なようです。

于 2012-09-24T16:29:46.120 に答える