当前位置: 首页 > 编程语言 > C#

为什么总是需要在具有IDisposable成员的对象上实现IDisposable?分享

时间:2023-04-10 23:56:03 C#

为什么总是需要在具有IDisposable成员的对象上实现IDisposable?据我所知,这是一个公认的规则,如果你有一个具有IDisposable成员m的类A,A应该实现IDisposable并且它应该在其中调用m.Dispose()。我找不到令人满意的理由说明为什么会这样。我理解的规则是,如果你有非托管资源,你应该提供一个终结器和IDisposable,这样如果用户没有显式调用Dispose,终结器仍然会在GC期间被清理。但是,根据该规则,您似乎不需要针对此问题的规则。例如...如果我有课程:classMyImage{privateImage_img;...}惯例说我应该有MyImage:IDisposable。但是,如果Image遵循约定并实现了终结器,而我不关心资源的及时释放,那又有什么意义呢?更新我在这里找到了一个很好的讨论。但是,如果Image遵循约定并实现了终结器,而我不关心资源的及时释放,那又有什么意义呢?然后没有一个,如果你不关心及时释放,你可以确保一次性对象被正确编写(事实上我从来没有做过这样的假设,即使使用MS代码也没有。你永远不知道你什么时候一不小心滑倒了)。关键是你应该关心,因为你永远不知道什么时候会导致问题。考虑一个打开的数据库连接。让它闲置意味着它不会在池中被替换。如果您收到多个请求,您可能会用完。如果你不在乎,没有人说你必须这样做。可以把它想象成在非托管程序中释放变量。您不必这样做,但这是非常明智的。如果没有别的原因,继承程序的人也不必奇怪为什么没人管,然后想办法把它清除掉。但是,如果Image遵循约定并实现了终结器,而我不关心资源的及时释放,那又有什么意义呢?你完全错过了Dispose的重点。这与您的方便无关。这是关于其他可能想要使用这些非托管资源的组件的便利性。除非你能保证系统中没有其他代码关心资源的及时释放,而用户也不关心资源的及时释放,否则应该尽快释放资源。这是一件很有礼貌的事情。在经典的囚徒困境中,合作世界中的孤独叛逃者收获颇丰。但在你的情况下,作为一个孤独的叛逃者只能通过编写低质量、最佳实践被忽略的代码来节省你的时间。这是你的用户和他们使用的所有程序,而你几乎什么也得不到。您的代码利用了一些其他程序解锁文件并释放互斥量和所有内容的事实。做一个好公民,为他们做同样的事情。这并不难,而且它使整个软件生态系统变得更好。更新:这是我的团队正在处理的真实情况的示例。我们有一个测试工具。它有一个“句柄泄漏”,因为一堆非托管资源没有被主动处理;每个“任务”可能泄漏六个句柄。当它发现禁用的测试等时,它会维护一个“要执行的任务”列表。我们在这个列表中有10到20000个任务,所以我们很快就会有这么多未完成的句柄——句柄应该已经死掉并释放回操作系统——很快系统中就没有代码了,也没有任何相关的东西测试可以运行。测试代码不关心。它工作得很好。但是最后被测试的代码做不出消息框或者其他UI,导致整个系统挂掉或者崩溃。垃圾收集器没有理由知道它需要更积极地运行终结器以更快地释放这些句柄;为什么要呢?它的工作是管理内存。您的工作是管理句柄,因此您必须完成这项工作。首先,无法保证终结器线程何时会清理对象-考虑类具有sql连接引用的情况。除非您确保及时处置,否则您将打开连接一段未知的时间-并且您将无法重用它。其次,终结不是一个廉价的过程——你应该确保如果你的对象被正确处置,你调用GC.SuppressFinalize(this)来阻止终结。扩展“非廉价”方面,终结器线程是高优先级线程。如果你给它做太多事情,它会占用你主应用程序的资源。编辑:好吧,这是ChrisBrummie关于定稿的博客文章,包括它为何如此昂贵。(我知道我在某处读到过这个)如果你不关心资源的及时释放,那真的没有意义。如果您可以确定该代码仅供您使用,并且您有足够的可用内存/资源,为什么不让GC在选择时执行。OTOH,如果其他人正在使用您的代码并创建(比如说)MyImage的许多实例,除非处理得当,否则将很难控制内存/资源的使用。许多类需要调用Dispose以确保正确性。例如,如果某些C#代码使用带有“finally”块的迭代器,如果迭代器用于创建枚举器而不处理枚举器,则该块中的代码将不会运行。虽然确保在没有终结器的情况下清理对象在某些情况下是不切实际的,但对于大多数依赖终结器来正确操作或避免内存泄漏的代码来说,这是糟糕的代码。如果您的代码获得了IDisposable对象的所有权,那么除非对象的cleass是密封的或者您的代码通过调用构造函数(而不是工厂方法)创建对象,否则您无法知道对象的真实类型是什么,以及是否放弃是安全的。Microsoft最初可能打算删除任何类型的对象都应该是安全的,但这是不现实的,而且认为删除任何类型的对象应该是安全的也无济于事。如果一个对象订阅了事件,那么允许安全放弃将需要为所有事件添加一个弱级别的间接,或者为所有其他访问添加一个(非弱)级别的间接。在许多情况下,要求调用者正确处置对象比增加大量开销和复杂性以允许放弃要好。另请注意,即使该对象试图适应遗弃,它仍然可能非常昂贵。创建一个Microsoft.VisualBasic.Collection(或随便你怎么称呼它),添加一些对象,然后创建和处理一百万个枚举器。没问题-它执行得非常快。现在创建和删除一百万枚枚举器。除非你每隔几千名调查员就强制执行一次GC,否则将会有很多贪睡节目。集合对象被编写为允许豁免,但这并不意味着它没有显着的成本。如果您正在使用的对象实现了IDisposable,这就是在告诉您在使用完它后有重要的事情要做。释放非托管资源或从事件中取消非常重要,这样事件在您认为已经完成后才不会被处理等等。通过不调用Dispose,您表示您比原作者更了解对象的运行方式。在一些微小的边缘情况下,如果您自己编写IDisposable类,或者如果您知道与调用Dispose相关的错误或性能问题,这实际上可能是正确的。通常,不太可能忽略需要您在完成后处理它的类。谈论终结器——正如已经指出的那样,它们有成本,如果对象使用SuppressFinalize,可以通过处置对象来避免这种成本。不仅仅是运行终结器本身的成本,还有在GC可以收集对象之前必须等到终结器完成的成本。具有终结器的对象在此集合中存活,其中对象被标识为未使用且需要被终结。所以它会被提升(如果它还没有在第2代中)。这会产生几个连锁反应:显然,如果您只对一个对象执行此操作,则不太可能产生任何可观的成本。如果您这样做是因为您发现正在对您正在使用的对象调用Dispose,那么它可能会导致上述所有问题。处理就像锁上前门。这可能是有原因的,如果你要离开大楼,你应该锁上门。如果锁定不是一个好主意,则不会有锁定。即使您不关心这种特殊情况,您仍然应该遵循标准,因为在某些情况下您会这样做。与使用有时被忽视的标准相比,设置标准并始终根据特定指南遵循它要容易得多。随着团队的成长和产品的老化,这一点尤其如此。以上是C#学习教程:为什么总要在有IDisposable成员的对象上实现IDisposable?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: