我附加到第三方,长期生活已删除的事件发布者,由于事件处理程序,最终使我可能短暂的对象保持活着状态。 Deleted事件很可能永远不会被触发,如果是,我只需要处理。从Deleted事件中取消订阅的位置并不明显,因此我想对它进行弱引用,这样我的对象就可以进行GC了。
我已经看到了许多非常精细的方法来创建弱事件处理程序,但是下面的代码段似乎可以解决这个问题,至少在提供的测试片段中是这样。这只是疯了还是可以起作用?
(http://diditwith.net/CommentView,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx在“A First Stab”下说类似的片段“(...)不足以与事件(...)一起使用”,为什么不呢?)
public static class WeakEvent
{
private class WeakEventHolder<TArgs> where TArgs : EventArgs
{
private readonly WeakReference _handler;
public WeakEventHolder(Action<object, TArgs> handler)
{
_handler = new WeakReference(handler);
}
public void Handle(object sender, TArgs args)
{
Action<object, TArgs> handler = (Action<object, TArgs>)_handler.Target;
if (handler != null)
handler(sender, args);
}
}
public static EventHandler MakeHandler(Action<object, EventArgs> handler)
{
return new WeakEventHolder<EventArgs>(handler).Handle;
}
}
测试课
[TestFixture]
public class Tests
{
public class Publisher
{
public EventHandler Event;
public void Raise()
{
if (Event != null)
Event(this, EventArgs.Empty);
}
}
public class Target
{
public Target(Publisher publisher)
{
publisher.Event += WeakEvent.MakeHandler(HandleEvent);
}
public void HandleEvent(object sender, EventArgs args)
{
System.Diagnostics.Trace.WriteLine("HandleEvent");
}
}
[Test]
public void Test()
{
Publisher publisher = new Publisher();
WeakReference wref = new WeakReference(new Target(publisher));
GC.Collect();
publisher.Raise();
Assert.False(wref.IsAlive);
}
}
答案 0 :(得分:1)
因为Action<object, TArgs> handler
可能在它的目标之前收集了垃圾。这是一个暴露问题的单元测试:
public class Bar
{
public void Foo(object sender, EventArgs args)
{
}
}
[Test]
public void ActionIsNotGCedBeforeTarget()
{
Bar bar = new Bar();
Action<object, EventArgs> action = bar.Foo;
WeakReference weakRef = new WeakReference(action);
action = null;
GC.Collect();
Assert.IsTrue(weakRef.IsAlive); // Will be false
}