我如何说服我的同事不要在任何事情上实施IDisposable?
我在一个项目中工作,在这个项目中有大量的对象被一些在应用程序的整个生命周期中保留在内存中的类实例化。 有很多内存泄漏是由于OutOfMemoryException被一次又一次地抛出引起的。 看起来在实例化对象超出范围之后,它们不会被垃圾收集。
我把问题分离出来主要是关于附属于永远不会分离的长寿命对象的事件处理程序,从而导致长寿命对象仍然有超出范围对象的引用,这些永远不会被引用垃圾收集。
我的同事提出的解决方案如下:在所有类,全局和Dispose方法中实现IDisposable,清空对象中的所有引用,并从您附加的所有事件中分离。
我相信这是一个非常糟糕的主意。 首先,因为它是“过度的”,因为问题可以通过修复几个问题区域来解决,其次,因为IDisposable的目的是释放对象控制的任何非托管资源,而不是因为不信任垃圾收集器。 到目前为止,我的论据都充耳不闻。 我怎么能说服他们这是徒劳的?
巧合的是,我刚刚在其他地方发表了此评论:
对不正确保留的对象的引用仍然是资源泄漏。 这就是为什么GC程序仍然有泄漏的原因,通常是由于观察者模式 - 观察者在列表上而不是观察者,并且永远不会被取消。 最终, remove
,需要为每个add
,就像delete
,需要为每一个new
。 完全相同的编程错误,导致完全相同的问题。 一个“资源”实际上只是一对函数,必须被称为具有相应参数的相同次数,而“资源泄漏”是当你不这样做时发生的情况。
你说:
IDisposable
的目的是释放您的对象控制的任何非托管资源
现在,事件上的+=
和-=
运算符实际上是一对函数,您必须使用相应的参数(事件/处理程序对是相应的参数)调用相同的次数。
因此它们构成资源。 由于GC没有为您处理(或“管理”)它,因此将它们视为另一种非托管资源会很有帮助。 正如Jon Skeet在评论中指出的那样,非托管通常具有特定的含义,但在IDisposable
的背景下,我认为将其扩大到包含任何类似资源的资源是很有帮助的 - 在“建立”之后必须“拆除” ”。
所以事件分离是使用IDisposable
进行处理的一个很好的选择。
当然,你需要在某个地方调用Dispose
,而不需要在每个对象(只有那些需要管理的事件关系)上实现它。
另外,请记住,如果一对对象通过一个事件连接起来,并且“抛弃它们”,则通过在所有其他对象中丢失对它们的所有引用,它们不会彼此活着。 GC不使用参考计数。 一旦物体(或物体岛)无法到达,它就会被收集起来。
您只需要担心事件处理程序中包含事件的对象已长时间处于活动状态。 比如AppDomain.UnhandledException
类的静态事件,或者应用程序主窗口上的事件。
将他们指向Joe Duffy关于IDisposable / finalizers的文章 - 许多聪明人的智慧结合。
我目前发现很难看到那里有一个声明说“当你不需要它时不要实现它” - 但除了其他任何事情之外,向他们展示正确实施它的复杂性可能有助于阻止他们从它...
不幸的是,如果人们不听,他们就不会听。 试着让他们解释为什么他们认为他们需要IDisposable
。 他们认为垃圾收集器不起作用吗? 告诉他们它的工作原理。 如果你能说服他们说它没有好处(对于大多数类型),那么他们肯定会停止为自己添加工作......
正如Brian所说,实现IDisposable
并不会帮助解决事件问题 - 它实际上需要被某种东西调用。 终结者在这种情况下也不会帮助你。 他们确实需要明确地做些事情来删除事件处理程序。
只是在所有类型中实现Dispose()
不会解决您的问题。 请记住, Dispose()
不会自动调用,它与回收托管内存无关。 为了使Dispose()
方法有任何效果,您需要在所有相关位置调用它 - 无论是显式还是using
。
换句话说,只是在所有地方实现IDisposable
都不会奇迹般地解决您的问题,因为除非您也更改代码中每种类型的用法,否则将不会调用Dispose()
方法。
但是,我不建议在所有类型上实现IDisposable
,因为它没有任何意义。 该接口用于指示所讨论的类型使用某些资源,而不是由垃圾回收器处理。
事件引用由垃圾收集器处理。 如果您的发布商的生活时间明显长于订阅者,您只需取消订阅即可。 一旦发布者死亡,用户也将死亡。
链接地址: http://www.djcxy.com/p/54529.html上一篇: How do I convince my colleagues not to implement IDisposable on everything?