我一直在使用 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 订阅一个事件,然后我删除对它的引用(闭包=空;)。
我期待 2 个 GC.Collect() 调用来清理我的旧闭包类,因此 WeakEventManager 将删除订阅者,而不运行处理程序,但测试失败。有任何想法吗?
编辑:对不起,通用参数不可见,现在它们是