在什么情况下从必要的事件中分离?

时间:2009-04-21 19:22:25

标签: c# .net events event-handling

我不确定我是否完全清楚附加对象中事件的含义。

这是我目前的理解,正确或精细:

1。附加到本地课程事件不需要分离

示例:

this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);

public event EventHandler OnMyCustomEvent = delegate { };

我假设当你的对象被处理或被垃圾收集时,这些函数会被释放并自动从事件中分离出来。

2。附加到不再需要的对象(= null;)必须与

分离

实施例: 附加到计时器的Elapsed事件,您只响应一次。我假设您需要将Timer存储在本地变量中,以便在事件触发后分离Elapsed事件。因此,在本地方法范围内声明计时器会导致泄漏:

System.Timers.Timer myDataTimer = new System.Timers.Timer(1000); myDataTimer.Elapsed += new System.Timers.ElapsedEventHandler(myDataTimer_Elapsed);

第3。将本地对象中的事件附加到您的班级不需要处理?

例如,如果您有一个ObservableCollection,您可以创建,监视并让它死掉。如果使用本地私有函数附加到CollectionChanged事件,当您的类被垃圾收集时,此函数是否会解除分配,导致ObservableCollection也被释放?

我确定我已经停止使用对象并且无法从事件中分离的地方(例如,我制作的计时器示例),所以我正在寻找关于它是如何工作的更清楚的解释。

3 个答案:

答案 0 :(得分:23)

我认为你使它变得比它需要的更复杂。你只需要记住两件事:

  • 当您订阅某个活动时,该活动的“所有者”(发布商)通常会保留对您订阅的代表的引用。
  • 如果使用实例方法作为委托的操作,则委托具有对其“目标”对象的引用。

这意味着如果你写:

publisher.SomeEvent += subscriber.SomeMethod;

然后subscriber将无法在publisher之前进行垃圾回收,除非您稍后取消订阅。

请注意,在许多情况下,subscriber只是this

publisher.SomeEvent += myDataTimer_Elapsed;

相当于:

publisher.SomeEvent += this.myDataTimer_Elapsed;

假设它是一个实例方法。

由于事件订阅,没有反向关系 - 换句话说,订阅者不会让发布者保持活力。

顺便提一下,请参阅my article on events and delegates

答案 1 :(得分:3)

防止垃圾收集的其余引用还有一个可能很明显的效果,但在此主题中尚未说明;附加的事件处理程序也将被执行。

我经历了几次。一个是当我们有一个应用程序,它运行的时间越长越慢。应用程序通过加载用户控件以动态方式创建用户界面。容器使用户控件订阅环境中的某些事件,其中一个在控件被“卸载”时没有取消订阅。

一段时间后,这导致每次引发特定事件时都会执行大量事件侦听器。当大量“睡眠”实例突然醒来并尝试对同一输入采取行动时,这当然会导致严重的竞争条件。

总之;如果你编写代码来挂钩事件监听器;确保你不再需要时尽快释放。我几乎敢于承诺,它将在未来的某个时刻让你免于至少一次头痛。

答案 2 :(得分:1)

您必须取消订阅活动的相关案例如下:

public class A
{
    // ...
    public event EventHandler SomethingHappened;
}

public class B
{
    private void DoSomething() { /* ... */ } // instance method

    private void Attach(A obj)
    {
       obj.SomethingHappened += DoSomething();
    }
}

在这种情况下,当你处理一个B时,obj的事件处理程序仍会有一个悬空引用。如果要回收B的内存,则需要先从相关的事件处理程序中分离B.DoSomething()

如果事件订阅行看起来像这样,你可能遇到同样的事情:

obj.SomethingHappened += someOtherObject.Whatever.DoSomething();

现在它是someOtherObject挂钩,无法进行垃圾回收。