将所有标准事件处理程序替换为 WeakEventManager 或其变体是否安全?

Is it safe to replace all standard event handler to WeakEventManager or its variants?

标准事件处理程序(使用运算符+=)是内存泄漏原因之一(如果不是unregistered/disposed(使用-= 运算符)).

Microsoft 使用 WeakEventManager 及其继承解决了它,例如:PropertyChangedEventManager, CollectionChangedEventManager, CurrentChangedEventManager, ErrorsChangedEventManager 等等。

内存泄漏的简单示例代码为:

public class EventCaller
{
    public static event EventHandler MyEvent;
    
    public static void Call()
    {
        var handler = MyEvent;
        if (handler != null)
        {
            handler(null, EventArgs.Empty);
            Debug.WriteLine("=============");
        }
    }
}

public class A
{
    string myText;

    public A(string text)
    {
        myText = text;
        EventCaller.MyEvent += OnCall;

        // Use code below and comment out code above to avoid memory leakage.
        // System.Windows.WeakEventManager<EventCaller, EventArgs>.AddHandler(null, "MyEvent", OnCall);  
    }
    
    void OnCall(object sender, EventArgs e)
    {
        Debug.WriteLine(myText);
    }
    
    ~A()
    {
        Debug.WriteLine(myText + " destructor");
    }
}

void Main()
{
    var a = new A("A");
    var b = new A("B");
    EventCaller.Call();
    a = null;
    GC.Collect();
    EventCaller.Call();
}

输出为:

A
B
+++++++
A
B
+++++++

我们可以看到析构函数不会被调用。 但是如果我们改变(通过注释未使用的代码)从:

    EventCaller.MyEvent += OnCall;

    System.Windows.WeakEventManager<EventCaller, EventArgs>.AddHandler(null, "MyEvent", OnCall);  

输出为:

A
B
+++++++
B
+++++++
A destructor
B destructor

A 为空后,将不再调用其事件处理程序。 A 和 B 将在没有 -= 运算符的情况下不再使用后被处理。

  1. 我可以安全地将所有 += 运算符替换为 System.Windows.WeakEventManager 以避免由于可能丢失事件注销和保存代码而导致的内存泄漏不应该实现 IDisposable?

  2. 如果不是真的安全,我应该考虑或注意什么?

1) 我不会将代码保存作为使用 Wea​​kEventManager 而不是实现 IDisposable 的参数。

2) 在弱事件模式的情况下,事件处理将继续,直到垃圾收集器收集到监听器。 取消引用侦听器不会立即停止事件处理,就像显式取消注册 Dispose 模式中的强引用事件处理程序一样。

3) 请参阅有关 Weak Event Patterns

的 Microsoft 文档

The weak event pattern can be used whenever a listener needs to register for an event, but the listener does not explicitly know when to unregister. The weak event pattern can also be used whenever the object lifetime of the source exceeds the useful object lifetime of the listener. (In this case, useful is determined by you.)

如果您明确知道何时注销侦听器,我更喜欢标准事件并实施 Dispose 模式。从事件处理程序的角度来看,显式取消注册的优点是事件处理立即停止,而弱事件模式继续处理事件(也可能是 CPU 和内存消耗),直到垃圾收集器收集监听器.

Can I safely replace all += operator with System.Windows.WeakEventManager to avoid memory leakage due to probably missing event unregistration and saving code by should not implement IDisposable?

可以吗?大概。你应该?可能不会。如果您确实有对事件处理程序的强引用,如果事件的发布者比事件的订阅者存在时间更长,您应该更愿意取消订阅它,而不是用弱事件替换强引用。使用弱事件有副作用。其中之一是性能。另一个是语义差异。关于.NET Framework 中事件的实现为什么默认不使用弱事件模式,大家可以参考以下问答:

当然在某些情况下您应该使用弱事件模式。一种这样的情况是 WPF 中的数据绑定,其中源对象完全独立于侦听器对象。但这并不意味着您应该始终使用弱事件模式。这也不意味着您应该停止关心在您的应用程序中处理订阅。

post by Thomas Levesque:

中指出了您不能“盲目地”用弱事件替换正常事件的特定情况

If you're subscribing to the event with an anonymous method (e.g. a lambda expression), make sure to keep a reference to the handler, otherwise it will be collected too soon.

因此,只要您另外保留对匿名处理程序方法/委托的引用,就可以将其更改为 weak。