我不确定我是否完全清楚附加对象中事件的含义。
这是我目前的理解,正确或精细:
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也被释放?
我确定我已经停止使用对象并且无法从事件中分离的地方(例如,我制作的计时器示例),所以我正在寻找关于它是如何工作的更清楚的解释。
答案 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
挂钩,无法进行垃圾回收。