0

私はこの問題に頭を悩ませようとしています。以下のコードは、最終的には、SubscribersClass 型のガベージ コレクション オブジェクトの数をカウントするだけです。コードが示されているように実行されると、SubscribersClass.Count の値が 0 になります。EventsClass の最初の行をコメント アウトし、そのクラスの残りのコメントを解除すると、SubscribersClass.Count の値は 10 になります。

私が思いつく唯一のことは、EventsClass EventHandler に問題があるため (示されているように)、SubscribersClass のインスタンスが実際に作成されていないということです。

誰かが何が起こっているのかを正確に理解するのを手伝ってくれることを望んでいました.

これは学術的なものであり、実用的な価値はありません。それを理解するために結ぶだけですが、これまでのところGoogleBlistersを取得することしかできませんでした.

namespace understandingEvents
{
    public class EventsClass
    {
        public event EventHandler SomeEvent;  // if this is commented out and
                                              // remainder of class is uncommented
                                              // it works fine
        /*
        public event EventHandler SomeEvent
        {
             add
             {
                 Console.WriteLine("An event has been added");
             }
             remove
             {
                 Console.WriteLine("An event has been removed");
             }
         }
         */
    }

    public class SubscribersClass
    {
        static int count = 0;

        static public int Count
        {
            get { return count; }
        }

        public  SubscribersClass (EventsClass eventPublisher)
        {
            eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
        }

        ~SubscribersClass()
        {
            Interlocked.Increment(ref count);
        }

        public void Subscriber_SomeEvent(object sender, EventArgs e)
        {
            Console.WriteLine("This is an event");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            EventsClass publisher = new EventsClass();
            for (int i = 0; i < 10; i++)
            {
                SubscribersClass subscriber = new SubscribersClass(publisher);
                subscriber = null;
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.WriteLine(SubscribersClass.Count.ToString());
        }
    }
}
4

3 に答える 3

3

標準のコンパイラ実装イベント ( public event EventHandler SomeEvent;) を使用する場合、デリゲートはSubscribersClass.

これは、publisherがまだ each and every への参照を保持しているsubscriberため、決して解放されないことを意味します。

独自のハンドラーを作成する場合addremove実際にはデリゲートを使用していないため、デリゲートは無視されます (イベントを発生させても効果がなく、その場合、サブスクライバーによって処理されないことがわかります)。これらの参照は保持されないため、GC.Collect呼び出しでサブスクライバーを「クリーンアップ」できます。これにより、カウントが増加します。

于 2013-08-22T18:48:20.220 に答える
2

イベントをサブスクライブすると、パブリッシャーはサブスクライバーへの参照を保持します。これにより、パブリッシャーは、イベントが発生したときに、イベントにサブスクライブされたすべてのメソッドを呼び出すことができます。

問題を解決するには、オブジェクトを破棄する前にサブスクリプションを削除してください。(イベントのパブリッシャーへの参照を保存する必要があります)。Unsubscribe メソッドを手動で呼び出すか、(より良い) IDisposable を実装して Dispose() で unsubscribe することで、これを行うことができます。

public  SubscribersClass (EventsClass eventPublisher)
    {
        m_eventPublisher = eventPublisher;
        eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
    }


public override void Dispose()
{
        m_eventPublisher.SomeEvent -= Subscriber_SomeEvent;
}
于 2013-08-22T18:56:53.007 に答える
0

SubscribersClassインスタンスが収集されないため、テスト コードでイベント サブスクライバーを削除していません。

コメント アウトされたコードはリスナーをまったく追加しないため、 のすべてのインスタンスは、SubscribersClass作成されるとすぐに GC の準備が整います。

コード (適切に修正された場合) は、すべての変数の有効期間が延長されるため、DEBUG ビルドでも異なる動作をすることに注意してください。すべての興味深いコードを関数に入れ、その外側で GC をトリガーすることを検討してください。

于 2013-08-22T18:52:23.427 に答える