【问题标题】:About the Dispose pattern and the Finalizer in C#关于 C# 中的 Dispose 模式和终结器
【发布时间】:2013-01-11 21:36:26
【问题描述】:

首先在this MSDN page

有一个标准的 Dispose 模式。并且有一个bool作为protectedDispose方法的参数来告诉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(),在终结器中什么都不做,否则在终结器中清理非托管资源。在这里我们不需要关心那些托管资源。

【问题讨论】:

  • 阅读这篇精彩的文章以全面了解 Dispose 模式。 codeproject.com/Articles/29534/… 初读相当复杂,但很有启发性。
  • 想象你的一个类管理资源本身实现了IDisposable。你认为你应该在哪里打电话给Dispose()

标签: c# destructor dispose finalizer


【解决方案1】:

if (disposing) {} 块中究竟应该做什么?

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

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

不,不是那个意思。这只是关于 IDisposable 对象。

语言中只有一个析构函数(或终结器)不是更好吗?

我们只有一个析构函数,也就是终结器,Dispose() 不是。这使您段落的其余部分无关紧要。

我们有 Disposable 模式和 GC,它们是相关的和合作的,但并不相同。 GC 管理内存并且只管理内存。 IDisposable 用于管理资源(流、连接、位图)。

【讨论】:

  • 我总是觉得 null-comparable-to-disposing 的想法是一个任何人都会凭直觉得出的奇怪结论
  • @henk-holterman 感谢您对if (disposing){} 块的具体说明!无论如何,通过析构函数或终结器,我的意思是将所有破坏例程放在一起,这样就不需要关心终结器和处置接口。我仍然需要通过我的 onw 来说明这一点。
【解决方案2】:

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

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

如果您的某个字段或属性是一次性的,那么您只需实现 IDisposable 模式。您不必完全按照上述模式进行操作。

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

在这种情况下,您需要考虑清理对象的两个地方。要么像他应该的那样由开发人员调用Dispose() 来清理它,要么他会忘记。如果他忘记了,而你有一个析构函数,那么它就会被放到一个终结队列中。

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

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

【讨论】:

  • 警告“如果您的字段或属性之一是一次性的......”段落 - 如果该类不是 sealed(不幸的是,不是默认值),并且您正在实施一次性,你应该全力以赴,因为你不知道派生类需要什么或做什么。
  • @MiketheTike 感谢您的明确回答,阅读您的回答后,我确实有了更好的理解。所以我认为终结器只是确保清理非托管资源,无论程序员是否调用Dispose。而且由于还要清理托管资源和非托管资源,我们需要分别处理Dispose 和终结器中的托管资源。
  • @Damien_The_Unbeliever:这是微软第一次编写Dispose 模式时的想法。然而,在实践中,如果基类不封装任何非托管资源,派生类也不应该封装。他们可能需要的任何非托管资源都应该封装在他们自己的可终结类中(可能嵌套在派生类中),然后派生类可以将其用作托管资源。
猜你喜欢
  • 1970-01-01
  • 2010-12-07
  • 1970-01-01
  • 2016-04-27
  • 2014-03-13
  • 2011-04-17
  • 2011-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多