0

我正试图解决这个问题。下面的代码最终只计算订阅者类类型的垃圾收集对象的数量。当代码如图所示运行时,我得到的 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.

这意味着 thatpublisher仍然持有对 each 的引用subscriber,因此它们永远不会被释放。

当您编写自己的addremove处理程序时,您实际上并没有使用委托,因此委托被忽略(您会发现在这种情况下引发事件没有效果并且订阅者不会处理),这也意味着那些引用没有被保留,所以GC.Collect调用可以“清理”订阅者。这会导致计数增加。

于 2013-08-22T18:48:20.220 回答
2

当您订阅事件时,发布者将持有对订阅者的引用。这就是允许发布者在事件发生时调用所有订阅事件的方法的原因。

要解决您的问题,只需在销毁对象之前删除订阅即可。(您需要存储对事件发布者的引用)。您可以通过手动调用一些 Unsubscribe 方法或(更好地)实现 IDisposable 并在 Dispose() 中取消订阅来做到这一点。

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 回答