我刚开始使用.NET框架。今天,我被教导了IDisposable
界面和dispose()
方法。我被教了几件事:
dispose()
应包含与对象相对应的清理代码(如关闭任何对象占用的任何资源 - 文件或数据库连接等)。
我还被告知,如果我们不在dispose()
方法中执行此操作,则可以在析构函数中执行相同的操作,但这并不能确保立即执行,我们将受到怜悯GC。
如果我们根本不提供任何清理代码,GC将强制终止与我们的对象所持有的资源的所有连接。因此,我们应该自己处理清理代码。
但我很好奇CLR为什么不自行处理这个问题呢?它负责内存管理,它负责垃圾收集。因此,它应该非常清楚哪个Object保存在哪个资源上以及何时该Object死掉。那么,它应该能够解除分配这些资源吗?
我告诉了几个人这件事。我得到的答案是,因为我们需要优雅地关闭它,因为GC强行关闭它。实际上是原因吗?答案 0 :(得分:3)
在.NET中,远远超过GC知道的托管代码。就像涉及大量非托管代码一样:所有文件句柄,数据库连接,网络套接字......所有这些都是普通的非托管Win32代码。你甚至不相信在你漂亮的C#应用程序中调用的几乎每一个BCL函数中,你都会遇到大量用C ++编写的非托管函数(并且可能是上帝禁止VB6)并深入到OS的内部本身。所有这些功能都是分配非托管内存,句柄,......托管世界不知道那里发生了什么。
例如,每次打开文件(FileStream
)时,您基本上都在调用(当然是在幕后)CreateFile非托管Win32函数。此函数直接从文件系统分配非托管文件句柄。 .NET和GC严格无法跟踪这些非托管代码及其所做的一切。这就是为什么这些类实现IDisposable接口。因此,您始终可以将它们的实例包装在using statements中,并确保始终调用Dispose方法,即使在发生异常时也是如此,并且尽快进行此操作。 Dispose方法将负责调用另一个非托管函数来清理它创建的混乱。
所以基本上你可以考虑IDisposable接口的方式如下:
当我们使用完全托管语言(例如来自Microsoft Research的{{3}})编写操作系统的那一天,我们可能不再需要IDisposable,因为GC将能够完全替换它将了解该系统内发生的一切。
答案 1 :(得分:2)
IDisposable
和Dispose()
的要点是你应该清理非托管内存。这是内存.NET没有分配的,它来自外部来源,因此GC无法知道它。因此无法自动清理它。基本上,这正是托管和非托管内存之间的区别; - )
通常,您应该实现Dispose()
来清理您的类使用的任何非托管资源,并实现终结器以调用Dispose()
。不过,终结者只是一个保障。如果调用者忘记正确处理您的类,它将确保最终最终清理这些资源。
答案 2 :(得分:1)
IDisposable
界面可以为您提供清理未管理资源的方法。 CLR仅为您管理托管资源。
换句话说,CLR只知道如何清理它管理的东西。如果您打开与系统其余部分的连接(例如打开文件,数据库连接等),那么这些是您的责任,您需要告诉CLR您希望它如何为您清理它们。
答案 3 :(得分:1)
IDisposable
界面只是一种约定,允许您确定性地处理托管和非托管资源。它本身不会取代垃圾收集或做任何涉及垃圾收集器本身的事情。
非托管资源更明显,因为除非处理这些资源(无论是在终结器中还是使用确定性处理),否则它们将作为内存泄漏保留,直到进程结束。对于托管内存,如果您没有确定性地处理这些项目,那么GC将会不确定地收集它们(假设最终有资格收集),因为它们是受管理的(这也是部署模式不包含托管项目的原因)在终结者路线中。)
IDisposable
本身没有做任何事情,它只是一个公认的接口(在using
关键字的代码中受支持),人们希望在处理使用耗材资源的项目时找到它,不受管理记忆,外部物品等。
CLR无法知道外部项目何时结束。这完全取决于您的应用程序的流程。如果您碰巧也不知道何时处置对象,终结器语法很有用。如果在自定义类上实现终结器,则垃圾收集过程将在最终收集之前运行此终结器。这是你自己收拾整理的最后机会。
答案 4 :(得分:1)
它只能处理.NET对象的内存管理。任何需要使用非托管资源的代码(例如,因为它与C ++库交互)都不属于垃圾收集器的bailiwick。需要告诉所有代码什么时候以老式的方式释放资源。
答案 5 :(得分:1)
.Net框架(和GC)无法知道如何发布未受管理的资源。它所能做的就是破坏托管代码对资源的引用。实际上,在与数据库服务器的连接上调用.Close()(从而告诉它连接应该返回到可用连接的轮询),而不仅仅是销毁引用,并让它超时#&# 39;经过一段时间后自己拥有。
因此,只要有可能,请在引用未受管理的资源时使用IDisposable接口!
答案 6 :(得分:1)
IDisposable
。最常见的示例是连接或文件句柄。您不希望在释放文件之前等待GC运行,或者关闭与数据库的连接,因为您不知道何时会发生这种情况。
大多数人将IDisposable
与非托管资源相关联,这些资源大多是准确的,但是不记得终结器是处理这些资源的正确.NET方法。 IDisposable
提供了一种确定性处理方式,如果这对您的计划很重要。
答案 7 :(得分:0)
我们使用Dispose将非托管resssource作为文件访问或连接数据库处理,因为GC没有关于此非托管资源的信息。
你也可以使用Finalize,但它不是高效的,因为你将你的资源保存在finalization结构中,并且GC通过这个finalization结构在dispose周期结束时传递,并且它不是高效的