关于C#中的Dispose模式和Finalizer

时间:2013-01-28 14:11:01

标签: c# destructor dispose finalizer

首先在this MSDN page

有一个标准的Dispose模式。并且有一个bool作为受保护Dispose方法的参数,告诉GC是否已经手动释放了托管资源,因此GC不需要关心它们。

现在的问题是,在if (disposing) {}区块内究竟应该做些什么?通常,GC会清理托管资源,因此无需执行任何特殊操作。但是因为在这个块中,需要明确清理托管资源,这是否意味着只需将对象中的所有字段和内容设置为null

其次,在语言中只有一个析构函数(或者它所调用的终结函数)是不是更好?然后在GC设计中,只需稍微确定是否已经调用了析构函数,这样就不需要对其进行垃圾收集,或者还没有调用析构函数,GC应该清理它。我发现Dispose模式非常复杂,我很困惑要清理哪些函数以及如何清理派生类。通过使用signle析构函数设计,GC只是在它们尚未清理时清理它们,并且当它们已经清理时它们不会清理。

问候

PS:这也是清理对象的一个​​好而简单的模式吗?

class Foo
{
    bool unmanagedDisposed = false;
    void Dispose() {/*clean up unmanaged resources*/ unmanagedDisposed = true;}
    ~Foo() {if (!unmanagedDisposed) Dispose();}
}

因此,如果程序员知道并记得调用Dispose(),则无法在终结器中执行任何操作,否则将清除终结器中的非托管资源。在这里,我们不需要关心那些托管资源。

2 个答案:

答案 0 :(得分:1)

  

if (disposing) {}区块内应该做什么?

您清理托管的资源,即您在此时拥有的所有IDisposable对象上调用Dispose()

  

显式清理托管资源,这是否只是将对象中的所有字段和内容设置为null?

不,这并不意味着。它只与IDisposable对象有关。

  

在语言中只有一个析构函数(或者它所调用的终结函数)不是更好吗?

我们只有一个析构函数,即Finalizer,而Dispose()不是它。这使得你的段落的其余部分无关紧要。

我们有一次性模式和GC,它们是相关的,合作但不一样。 GC管理内存和仅内存。 IDisposable用于管理资源(流,连接,位图)。

答案 1 :(得分:1)

基本上,如果您的类具有非托管字段或属性,则只需要使用dispose实现完整的~Destructor()模式。

如果您管理的所有字段和属性都不是一次性的,那么您根本不需要实现IDisposable。

如果您的某个字段或属性是一次性的,那么您需要实现IDisposable模式。你不一定要用上面的模式来养猪。

如果在最后一种情况下你在一个字段或属性中有一个非托管资源(例如一个指向某个东西的本机指针,一个除ADO或其他托管连接之外的数据库连接),那么.net不知道如何清理它垃圾收集器四处滚动。

在这种情况下,您需要考虑清理对象的两个位置。开发人员会像他应该的那样打电话给Dispose()来清理它,或者他会忘记。如果他忘记了,并且你有一个析构函数,那么它就会被置于一个终结队列中。

这是Dispose(disposing)来电的地方。 如果开发人员很好并且调用了Dispose(),那么您发送的是true,以便也可以清理托管资源。

如果开发人员没有调用dispose并且穷人对象最终在终结队列上,那么在托管对象上调用Dispose()将抛出异常,因为它们不再存在。因此,在这种情况下,您发送一个False,以便跳过托管资源并避免异常。我不确定这一点,但是传说说在终结队列上抛出异常会退出整个过程,甚至可能会结束这个世界。所以不要这样做。