如果我理解正确的话,.Net运行时总是会在我之后进行清理.因此,如果我创建新对象并停止在代码中引用它们,运行库将清理这些对象并释放它们占用的内存.
既然如此,为什么有些对象需要析构函数或dispose方法呢?当它们不再被引用时,运行时不会清理它们吗?
如果我理解正确的话,.Net运行时总是会在我之后进行清理.因此,如果我创建新对象并停止在代码中引用它们,运行库将清理这些对象并释放它们占用的内存.
既然如此,为什么有些对象需要析构函数或dispose方法呢?当它们不再被引用时,运行时不会清理它们吗?
需要终结器来保证将稀缺资源释放回系统,如文件句柄、套接字、内核对象等.因为终结器总是在对象生命周期结束时运行,所以它是释放这些句柄的指定位置.
Dispose
模式用于提供确定性的资源销毁.由于.Net运行时垃圾回收器是不确定的(这意味着您永远不能确定运行时何时收集旧对象并调用它们的终结器),因此需要一种方法来确保系统资源的确定性释放.因此,当您正确地实现Dispose
模式时,您提供了资源的确定性释放,并且在使用者粗心大意而没有处置对象的情况下,终结器将清理对象.
为什么需要Dispose
的一个简单例子可能是一种快速而肮脏的日志(log)方法:
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
对象上的终结器.这带来了一个问题,因为在此期间,可能会再次调用该方法来写入日志(log),但这一次它将失败,因为文件仍处于锁定状态.
正确的方法是在使用完对象后将其释放:
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.
}
顺便说一句,从技术上讲,终结器和析构函数的意思是一样的;我更喜欢调用C++的析构函数终结器,因为否则它们会把人们与C++析构函数混淆,而不像C++,析构函数是确定性的.