我正试图解决这个问题。下面的代码最终只计算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());
}
}
}
答案 0 :(得分:3)
当您使用标准的,编译器实现的事件(public event EventHandler SomeEvent;
)时,订阅该事件会导致在该事件中保留对订阅者的引用,因为该委托引用了{{1的实例方法}}
这意味着SubscribersClass
仍然保留对每个publisher
的引用,因此它们永远不会被释放。
当您编写自己的subscriber
和add
处理程序时,您实际上并未使用该委托,因此会忽略该委托(您会发现提升该事件没有任何效果且不是'在这种情况下由订户处理),这也意味着那些引用没有被保留,因此remove
呼叫可以“清理”订户。这会导致计数增加。
答案 1 :(得分: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;
}
答案 2 :(得分:0)
您没有删除测试代码中的事件susbscribers,因此未收集SubscribersClass
个实例。
注释掉的代码根本不会添加侦听器,因此SubscribersClass
的所有实例都会在创建后立即为GC准备好。
请注意,由于所有变量的生命周期延长,代码(如果正确修复)在DEBUG构建中的行为也会有所不同。考虑将所有有趣的代码放在函数中并在其外部触发GC。