我一直在使用WeakEventManager来避免内存泄漏,但是我开始过度使用它们了。 例如,我为INotifyPropertyChanged创建了扩展方法:
public static void AddWeakPropertyChanged(this INotifyPropertyChanged item, Action handler)
{
PropertyChangedEventManager.AddHandler(item, (s, e) => handler(e.PropertyName), string.Empty);
}
现在我很快意识到这样做是不起作用的。实际上,您不能真正使用匿名方法来进行弱事件处理。(如果我理解正确的话,编译器会为它创建一个'闭包类'(来保存引用的值),其中包含处理程序,但由于您的闭包类没有在任何地方引用,GC将清除它,并且事件处理程序将不会被调用)。
问题#1:这是正确的吗?我的意思是说,当使用匿名方法(或lambda)作为弱事件处理程序时,只有在GC未运行的同时(例如,它是不确定的)才会调用处理程序?
嗯,我认为是这样的,所以我进行了一些单元测试,以确保我理解得正确。在进行以下单元测试之前,它似乎都很好。
class DidRun
{
public bool Value { get; set; }
}
class TestEventPublisher
{
public event EventHandler<EventArgs> MyEvent;
public void RaiseMyEvent()
{
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
}
}
class TestClosure
{
public DidRun didRun { get; set; }
public EventHandler<EventArgs> Handler { get; private set; }
public TestClosure()
{
this.Handler = new EventHandler<EventArgs>((s, e) => didRun.Value = true);
}
}
[TestMethod]
public void TestWeakReference()
{
var raiser = new TestEventPublisher();
var didrun = new DidRun();
var closure = new TestClosure { didRun = didrun };
WeakEventManager<TestEventPublisher, EventArgs>.AddHandler(raiser, "MyEvent", closure.Handler);
closure = null;
GC.Collect();
GC.Collect();
raiser.RaiseMyEvent();
Assert.AreEqual(false, didrun.Value);
}
问题#2:有人能够解释一下为什么这个测试失败吗?
期望结果:在这里,我没有任何闭包(我将它们取出来,以确保发生了什么),我只是有一个对象(闭包),它使用WeakEventManager订阅事件,然后我放弃对它的引用(closure = null;)。
我希望两个GC.Collect()调用可以清理我的旧闭包类,因此WeakEventManager会取消订阅者并不运行处理程序,但测试失败了。有任何想法吗?
编辑:抱歉,泛型参数之前看不到,现在已经可见。