我有一个主窗口,我们将调用main
和一个子窗口,我们将调用child
,我们将在child
中调用用户控件control
。
程序从main
开始,用户执行一些操作来启动具有control
child
具有FileSystemWatcher
等功能。但是,我开始注意到FileSystemWatcher
事件在关闭后仍在child
触发。呃哦。所以我开始挖掘...
当我订阅Window_Closing
事件时child
它会按预期触发。 当我订阅UserControl_Unloaded
事件control
时,它会按预期触发(child
关闭后立即触发)。 所以我在control
上放了一个析构函数,这显然在关闭之前不会发生main
。
那么,为什么我的用户控件的析构函数在卸载用户控件时不释放属性呢?如果我知道哪些是相关的,我会包含代码片段......
好吧,在输入问题之后,StackOverflow的出色功能向我展示了这个问题,它有一个不错的通用答案。
我的解决方案是取消订阅UserControl_Unloaded
事件中的所有FileSystemWatcher
事件。 仅将观察程序对象设置为 null 是不够的。 对于像 .NET 这样的托管语言来说,这似乎工作量太大了,但我想不是。
FileSystemWatcher
实现IDisposable
。这通常表示类使用非托管资源(如文件句柄或非托管内存),垃圾回收器无法自动(或及时)回收这些资源。IDisposable
提供了一种Dispose
方法,此类类可以使用该方法正确释放其非托管资源。当然,清理也可以通过其他方法完成,但是IDisposable
"约定"使程序员更容易看到哪些类需要清理。这也意味着"dispose"一词在 C# 中具有非常具体的含义。
看起来,如果不处理掉FileSystemWatcher
,它仍然活着。由于您没有删除这些事件处理程序,因此它仍然包含对用户控件的引用,因此该控件也保持活动状态,并且您继续看到引发的事件。删除事件处理程序是一个好主意(尤其是当引发事件的对象具有较长的生命周期时),但在这种情况下,它只能解决症状,而不是根本问题:它允许用户控制被垃圾回收器回收,但FileSystemWatcher
仍然有效。
至于析构函数(它们在 C# 中通常称为终结器),它们很少需要。它们可以被实现IDisposable
的类用作最后的手段 - 以防万一程序员忘记调用Dispose
- 但它们有一点成本(终结器需要在队列中注册,它们由特殊的终结器线程调用),并且您无法控制它们的确切执行时间。