如果我理解正确的话,.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++,析构函数是确定性的.

.net相关问答推荐

如何在PowerShell中隐藏任务延迟输出?

Npgsql Minimum Pool Size 似乎没有被考虑在内

如果只有一个 : 存在于字符串中,则提取冒号后的内容

PowerShell - 如果用户输入凭据,则查询 AD 时出错

无法加载文件或程序集 不支持操作. (来自 HRESULT 的异常:0x80131515)

编译时禁用 Dll 文化文件夹

使用 EPPlus 时如何设置列类型

.NET 世界是否有 Maven 替代方案或端口?

是否有用于 Windows / C# 开发的可嵌入 Webkit 组件?

数据库架构更改后更新 LINQ to SQL 类的最佳方法

DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") 返回上午时间而不是下午时间?

静态析构函数

Convert.ToBoolean 和 Boolean.Parse 不接受 0 和 1

如何允许程序集(单元测试)访问另一个程序集的内部属性?

形成两个列表并集的最简单方法

.NET 进程间通信的最佳 Select 是什么?

.NET 高级别的 .NET 4.0 和 .NET 4.5 的区别

自创建数据库以来,支持ApplicationDbContext上下文的模型已更改

为什么要使用 C# 类 System.Random 而不是 System.Security.Cryptography.RandomNumberGenerator?

判断任意字符串是否为有效文件名的最简单方法