5

标准事件处理程序(使用 operator +=)是内存泄漏原因之一(如果它不是未注册/处置(使用-=operator))。

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. 如果它不是真的安全,我应该考虑或注意什么?

4

3 回答 3

4

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

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

为什么 C# 中事件的实现默认不使用弱事件模式?

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

于 2017-04-04T10:42:19.477 回答
2

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

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

3) 参见微软关于弱事件模式的文档

每当侦听器需要注册事件时,都可以使用弱事件模式,但侦听器不明确知道何时取消注册。只要源的对象生命周期超过侦听器的有用对象生命周期,也可以使用弱事件模式。(在这种情况下,有用由您决定。)

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

于 2017-04-04T08:32:19.130 回答
0

Thomas Levesque在一篇文章中指出了一个不能“盲目地”用弱事件代替正常事件的特定情况:

如果您使用匿名方法(例如 lambda 表达式)订阅事件,请确保保留对处理程序的引用,否则它会很快被收集。

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

于 2021-02-12T15:22:04.410 回答