2

我正忙着对非确定性破坏感到困惑。在对另一个问题的回答中,我得到的建议是析构函数/终结器(我认为在 c# 中是相同的东西,即名为 ~classname() 的函数)很昂贵且不需要。但是看看这个例子,使用了一个析构函数,从评论中听起来它可能是至关重要的。任何人都对这一切如何组合在一起有一些建议,我应该从我的代码中删除析构函数吗?

再次感谢。

4

3 回答 3

3

如果您绝对必须在某个时候运行一些清理,则应该只包含一个终结器,无论它是否明确执行。在这种情况下,您应该始终以明确的方式及时执行清理,并且无论如何都应该抑制最终确定,以便“好”客户端不会看到任何性能损失。

如果你有一个非托管资源的直接句柄,你通常只需要一个终结器——如果你只有一个对另一个有资源句柄的类的引用例如FileStream),那么你应该把它留给另一个类来拥有一个终结器。

随着SafeHandle.NET 2.0 的出现,值得编写自己的终结器的情况确实非常罕见

终结器的性能损失是它们使您的对象的生存时间比它们需要的时间长:在第一个 GC 周期中,它们被认为有资格被收集,它们被放入终结器队列 - 并撞到下一代只是像任何其他在 GC 循环中幸存下来的对象一样。然后终结器将在另一个线程中运行(在某个时候),只有这样它们才有资格真正被收集。因此,他们不是(比如说)在第一个 gen1 集合中被收集,而是在此之前一直生活到下一个gen2集合,这可能要晚得多。

于 2010-11-29T11:33:42.117 回答
1

通常,在以下情况下实现析构函数很有用:当不能保证时,客户端代码将正确关闭所有资源(文件流、数据库连接等)。因此,如果客户端代码无法执行此操作,您将拥有将关闭它的代码,这比让资源保持打开状态要好。

于 2010-11-29T11:32:42.737 回答
1

当您直接处理非托管资源时,您只需要完整的 Disposable 模式。然后由您的调用代码来确保(几乎)从未使用过析构函数。

在处理托管资源(=非托管资源的间接所有权)时,析构函数是无用的:

class FileWrapper
{
    private FileStream fs;  // managed resource

    ~FileWrapper()
    {
         if (fs != null) 
           fs.Dispose();   // fs is already on the GC finalizer queue
    }
}

每当 GC 收集 FileWrapper 对象时,可以确定 fs 对象在同一批次中。所以调用 fs.Dispose() 是没用的,只测试 FileStream.Dispose() 的正确(允许多次调用)行为。

这里唯一有用的析构函数是 FileStream 中的析构函数。

于 2010-11-29T11:42:04.113 回答