如果我理解正确,.net运行时将始终在我之后清理.因此,如果我创建新对象并停止在我的代码中引用它们,运行时将清理这些对象并释放它们占用的内存.
既然这就是为什么一些对象需要有一个析构函数或dispose方法呢?当它们不再被引用时,运行时不会清理它们吗?
需要终结器来保证将稀缺资源释放回系统,如文件句柄,套接字,内核对象等.由于终结器总是在对象生命周期结束时运行,因此它是释放这些句柄的指定位置.
该Dispose
模式用于提供确定性的资源破坏.由于.net运行时垃圾收集器是非确定性的(这意味着您永远无法确定运行时何时将收集旧对象并调用其终结器),因此需要一种方法来确保系统资源的确定性释放.因此,当您Dispose
正确实现模式时,您可以提供资源的确定性释放,并且在消费者粗心并且不处理对象的情况下,终结器将清理对象.
Dispose
需要原因的一个简单示例可能是快速而脏的日志方法:
public void Log(string line) { var sw = new StreamWriter(File.Open( "LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)); sw.WriteLine(line); // Since we don't close the stream the FileStream finalizer will do that for // us but we don't know when that will be and until then the file is locked. }
在上面的示例中,文件将保持锁定状态,直到垃圾收集器调用StreamWriter
对象上的终结器.这提出了一个问题,因为在此期间,可能会再次调用该方法来写入日志,但这次它将失败,因为该文件仍然被锁定.
正确的方法是在使用它时完成对象的处置:
public void Log(string line) { using (var sw = new StreamWriter(File.Open( "LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))) { sw.WriteLine(line); } // Since we use the using block (which conveniently calls Dispose() for us) // the file well be closed at this point. }
BTW,技术上的终结器和析构器意味着同样的事情; 我更喜欢称c#destructors'终结器',否则它们往往会让人混淆C++析构函数,这与C#不同,是确定性的.
以前的答案很好,但让我再一次强调这里的重点.特别是,你这么说
如果我理解正确,.net运行时将始终在我之后清理.
这只是部分正确.事实上,.NET 只为一个特定资源提供自动管理:主存储器.所有其他资源都需要手动清理.1)
奇怪的是,主存在几乎所有关于程序资源的讨论中都处于特殊状态.当然有一个很好的理由 - 主存储器通常是最稀缺的资源.但值得记住的是,还有其他类型的资源,也需要管理.
1)通常尝试的解决方案是将其他资源的生命周期与代码中的内存位置或标识符的生命周期耦合 - 因此存在终结器.
只有在系统没有内存压力的情况下才会运行垃圾收集器,除非它确实需要释放一些内存.这意味着,您永远无法确定GC何时运行.
现在,想象一下你是一个数据库连接.如果你在GC之后清理GC,你可能会连接到数据库的时间比需要的时间长得多,导致奇怪的负载情况.在这种情况下,您希望实现IDisposable,以便用户可以调用Dispose()或使用using()来确保尽快关闭连接,而不必依赖可能在以后运行的GC.
通常,IDisposable在任何使用非托管资源的类上实现.