我正在阅读“C#语言”,第4版,它讲的是WeakReference
和Weak Event Pattern
:
CHRISTIAN NAGEL:内存泄漏通常是由于错误使用事件造成的。如果客户端对象附加到事件但不从它们分离,并且不再使用对客户端对象的引用,则垃圾回收器仍然无法回收客户端对象,因为发布者的引用仍然存在。这可以通过(1)在不再使用客户端对象时分离事件来避免,(2)使用
add
类的remove
和WeakReference
访问器的自定义实现委托,或(3)WPF与IWeakEventListener接口使用的Weak Event pattern
。
我对此有疑问:选项“(2)WeakReference
”带来 NO便利,与“option(1)明确分离事件”相比,因为使用{{1仍然需要显式调用WeakReference
和add
。
否则,即使其中一个事件处理程序的对象被指定为null,“孤儿”对象仍将响应该事件 - 这将导致意外行为。
注意:remove
仅以事件处理程序对象不会受事件发布者对象影响的方式帮助垃圾收集; WeakReference
不强制事件处理程序对象被垃圾收集。
类似的问题也适用于弱事件模式。
也许这有点抽象,以Josh Smith的Mediator模式(http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/)为例。
WeakReference
如果我们有
public class Mediator //...
{
public void Register(object message, Action<object> callback)
{
// notice: Mediator has no Unregister method
}
public void NotifyColleagues(object message, object parameter)
{
// ...
}
}
public class ObjectA //...
{
public string ObjectAText
{
get { return _objectAText; }
set
{
//...
_mediator.NotifyColleagues(MediatorMessages.ObjectASaidSomething, _objectAText);
}
}
}
public class ObjectB //...
{
//...
public ObjectB(Mediator mediator)
{
//...
_mediator.Register(
MediatorMessages.ObjectASaidSomething,
param =>
{
// handling event ObjectASaidSomething
});
}
}
由于ObjectA objectA = new ObjectA();
ObjectB objectB1st = new objectB();
objectA.ObjectAText = "John"; // objectB1st will respond to this event.
objectB1st = null; // due to delay of garbage collection, the object is actually still in memory
ObjectB objectB2nd = new objectB();
objectA.ObjectAText = "Jane"; // both objectB1st and objectB2nd will respond to this event!
?
但是如果WeakReference
类提供“取消注册”方法(实际上我实现了一个),“选项(2)Mediator
”与“选项(1)明确地分离事件”没有区别“ 。 (Mediator本身仍然是一个有用的模式,可以穿透WPF或MVVM组件层的层次结构)
答案 0 :(得分:5)
如果我理解你的要求,那就需要做一些澄清。
否则,即使分配了一个事件处理程序的对象 null,“orphan”对象仍然会响应事件 - 这会 引起意外行为。
不是真的。这不是意料之外的行为。如果你没有明确地注销它,那么完全可以调用该对象。
弱事件的整个想法是一个安全网,因为他们订阅了一个事件,因此不能将对象保留在内存中。当事件超出范围时,它与事件中的注销无关。
如果您需要执行稍后操作,请为订阅者使用IDisposable模式和“using”构造,或者明确取消订阅。
即。弱事件是一个非常具体问题的解决方案 - 允许垃圾收集对象,这些对象订阅了一个长生命对象(如GUI或某些静态类)。
即使在对象超出范围的那一刻,弱事件也与自动取消对象无关。
答案 1 :(得分:1)
如果事件订阅者和发布者都合作,则可以在.net中实现合理的弱事件模式,而无需反射或其他CLR技巧。如果事件的取消订阅方法在终结器线程调用时需要正确运行,事件订阅者可以单方面实现弱事件模式,但不幸的是,当订阅来自未知类的事件时,这种期望是不合理的(例如,一个INotifyPropertyChanged)。诀窍是任何对对象真正“感兴趣”的人都要对包装器进行强引用,对于事件处理程序和其他东西来保持对对象“胆量”的引用。包装器可以使用Finalize方法保存对内部和对象的引用,该方法将取消订阅事件。