C# 我是否需要手动移除我声明的事件处理程序?

3

好的,这里举个例子:

  1. 我有UserControl A、UserControl B、UserControl C和一个Windows表单。
  2. 只有UserControl A会启动这个Windows表单。
  3. UserControl C有[下一步]和[返回]按钮。
  4. 假设,UserControl A被声明了一个事件处理程序。在UserControl A中的一个函数将实际上引发事件调用,以执行UserControl C中的一个函数。
  5. 所以,在UserControl C中,我必须添加

" UserControlA.OneFunction += this.UserControlC_Function;"

  1. 如果我在UserControl C中点击下一步按钮,它将销毁UserControl A并添加新的UserControl B到Windows表单中。但我从未手动删除此事件处理程序。

UserControl A中的一个函数是调用者(事件被声明的地方)。
UserControl C中的一个函数是监听器。

所以,这些是我的问题:

  • 在UserControl A被销毁之前,我是否应该手动移除处理程序?
  • 这个User Control A自动销毁会自动删除之前声明的处理程序吗?
  • 我应该在哪里添加这个?

" UserControlA.OneFunction -= this.UserControlC_Function;"


请问您能否扩展第二行?比如说,我手动向我的Windows表单添加了一个事件处理程序,以便稍后在用户控件A中调用一个函数?谢谢。 - cgon
请澄清:哪个是事件源,哪个是监听器? - Henrik
5个回答

3
按照传统惯例,我们不这样做。并且由于在处理后不应调用任何事件,除非相关的控件行为异常,否则没有必要这样做。 不存在此类代码,至少从反编译器中看不到。

如果我总是按下“下一个”和“返回”按钮,会发生什么? - lannyboy
说:UserControl C 有 [下一步] 和 [上一步] 按钮来控制 UserControl A 和 UserControl B,就像一个设置向导。每次按下 [下一步] 按钮时,将执行 "ctlContent.ctlRBContent1_GetBuildName += this.GetBuildName;"。但是这个事件从未被删除。那会是什么呢? - lannyboy
然而,在失去对C的引用之前删除处理程序是一个好主意。在这种特殊情况下,这并不是真正的问题,但养成这个习惯总是有好处的。 - Rune FS
@Rune,你的意思是我可以在析构函数中这样做吗? - lannyboy
@lannyboy:一旦控件被释放,它就不能再被“正确地”使用了。它的消息循环被终止,其WndProc将被注销。因此,只要您正确地处理控件的释放,它应该会正常工作。 - Interarticle
@lannyboy:实际上,事件是一个委托的包装器(用于迟绑定方法调用),该委托对象存储在类的实例中。事件的委托(MulticastDelegate)维护一个内部委托列表(数组),每个指向事件处理程序的委托。因此,在已释放并正确编写的控件上未删除事件处理程序的最坏结果是内存泄漏(仅当程序编写不良时)。因此,除非您正在编写大型程序,否则不需要担心此问题。 - Interarticle

2

在这种情况下,您不需要删除处理程序,因为表单及其按钮都没有被外部代码引用,整个对象图将因此被垃圾回收。


2

是的,我的意思是“当您需要手动删除时……”。我已经进行了编辑。谢谢。 - ChrisNel52

0

如果表单被释放(假设没有其他对象引用所涉及的对象),那么不删除事件处理程序的风险很小,但最好在无法访问侦听对象之前始终删除事件处理程序(即所有引用对象的变量超出范围)否则可能会创建内存泄漏。

在您的情况下,情况并非如此(如果我理解您所描述的内容,代码将更清晰)。问题在于,如果将引用C对象的委托附加到A对象上的事件,然后失去对C的访问权限(例如,将新值分配给变量),则C将一直存在,直到A被垃圾回收。


0

如果事件发布者的内存生命周期与事件订阅者的有用生命周期没有限制,未取消订阅事件可能会导致内存泄漏。如果不是因为这样做很麻烦,那么事件订阅者在被处理时没有取消订阅所有事件,事件发布者在被处理时没有将所有事件订阅置为空值就没有任何理由了。然而,既C#也VB都没有提供方便的方法来完成这些事情,因此,人们必须权衡正确的订阅处理的麻烦和在许多情况下可以逃避它的事实。


是的,我现在明白你的意思了。所以在控件被释放之前,我们必须手动移除事件处理程序。 - lannyboy
@lannyboy:如果表单和控件同时被放弃,那么放弃订阅将不会导致内存泄漏;例如,如果一个控件持有对长期对象的更改通知事件的订阅,则将表单订阅到控件的事件可能会导致本来可能是“小”的内存泄漏变成更大的内存泄漏(因为控件将保持表单的活动状态,而表单可能会保持其他控件的活动状态等)。至于使用每次100字节的内存泄漏是否比使用每次1兆字节的内存泄漏更好或更坏... - supercat
...在这方面有不同的观点。如果将会有固定数量的创建-放弃周期(比如100个),使用总共10,000字节比使用100,000,000字节更好。另一方面,如果一个程序每分钟执行一次创建/放弃周期,如果每个这样的周期占用了1兆字节,那么在一天内就会明显出现问题。如果每个这样的周期只需要100字节,那么可以在运行程序一周的同时只使用额外的1兆字节存储空间,但是处理订阅会变得越来越慢,直到使系统崩溃。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接