.NET中的Finalize和Dispose方法有什么意义? (回答前详情)

时间:2009-04-15 17:20:09

标签: c# .net

我需要在拆除对象时清理资源,但我总是发现 Dispose Finalize <之间存在差异/ em> ,析构函数方法有点令人困惑。

我发现这篇精彩的文章简明扼要地描述了它们之间的区别,我将不得不保存以备将来参考:
"Difference between Destructor, Dispose and Finalize methods" - Sanjay Saini http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

我在这里要问的根本问题是这个。

  

如果一种语言提供析构函数(例如C#[refuted])那么   值 Dispose 和    最终确定 添加到等式中?

我只是一个习惯于以旧学校的方式在析构函数中做所有事情的笨蛋,或者是否有一些我错过的东西只能通过将一个物体拆成三部分来实现?

更新
正如一些回复中所指出的,C#实际上并没有析构函数。在这一点上,这个问题可能没有实际意义。当我在上面引用的文章中读到C#实际上有一个单独的解构器(显然是一个错误)时,它把我扔了一个循环,我开始想知道如果你有一个最终的析构函数来包装所有内容,Dispose和Finalize的意义是什么。我想在像C#这样的GC语言中,为对象提供denemount的单个析构函数的概念没有多大意义。

很抱歉你们中的一些人,但有几个人没有仔细阅读这个问题,并且认为我在询问Dispose和Finalize之间的区别,这真的不是重点。

9 个答案:

答案 0 :(得分:7)

该博客文章的作者有点困惑......

在C#中,没有“析构函数”这样的东西。只有终结者和IDisposable。

~ClassName()方法不称为“析构函数”。它被称为终结者。

Dispose exists用于从代码中释放资源,其中存在从GC调用的终结器。通常,终结器会调用Dispose()方法,但"Dispose Pattern"会将您设置为仅处理终结器中的非托管资源。

您会看到,当调用终结器时,您处于不同的线程,并且您拥有的任何托管对象都不一定有效。因此,如果从终结器中调用Dispose(),您应该调用Dispose(false),它告诉“Dispose Pattern”只处理非托管资源。

此外,"Dispose Pattern"表示调用Dispose(true)时,应该禁止该对象的终结器。

答案 1 :(得分:4)

只能自动完成托管对象。如果要提供隐式处理非托管对象,可以使用Finalizer。如果您想为对象的调用者提供显式处理控制权,您可以允许他们调用Dispose。

I like this article

答案 2 :(得分:3)

很难回答这个问题,因为您已经链接到解释差异的文章。

但是我还是试试。

使用垃圾收集,您将拥有非确定性的内存管理,并且由于GC将运行您的终结器,因此您可以保证但非确定性的资源管理。

这很好,因为你知道事情会被清理干净。

然而,这很糟糕,因为你不知道什么时候会发生。

例如,如果你打开一个文件并在打开它时将其锁定,不知道文件何时可以在以后再次打开是不好的,但保证在某些时候关闭它是好的。

Dispose和IDisposable通过添加确定性资源管理来处理这个不好的部分。您可以选择何时处置资源,关闭文件,网络连接,数据库连接等等。这是您的选择,您通常会在不再需要时处置该资源。因此,当您不再需要该文件时,您将处置保持打开的对象。该对象将保留在内存中(非确定性内存管理)一段时间,但是当您这样说时文件将被关闭,准备好立即再次打开。

因此,您获得了对资源的确定性处理,并将其与非确定性内存管理相结合,并且您获得了两个世界中最好的。

答案 3 :(得分:2)

C#中的析构函数(~Foo())实际上只是拼写“Finalize”的另一种方式。

也许事后看来,语法的选择并不是最好的,因为C#中的~Foo()与C ++中的~Foo()有很大不同。

答案 4 :(得分:2)

很抱歉回答我自己的问题,但我想我已经汇集了各种来源,包括一些答案,我在回答GC平台时解决了对象生命周期差异的问题的简明答案。


非GC对象生命周期:

初始化:在第一个引用添加到object时调用的构造函数中发生。

清理:在析构函数中发生,当最后一个变量引用对象超出范围或显式设置为nothing / null /另一个对象时调用析构函数。


GC对象生命周期:

初始化:在第一个引用添加到object时调用的构造函数中发生。

清理(第一遍):发生在Dispose方法中,必须由客户端显式调用或在Using语句结束时隐式调用。此清理特别用于释放必须立即释放的资源,而不是等待GC。

清理(最终通过):在Finalize方法中发生,该方法在GC的时间表上调用,可以延迟。如果实现了finalize方法,则会调用它,除非之前已在对象上调用GC.SuppressFinalize(通常来自dispose方法)以将其标记为已清除。


我做对了吗?如果我在这里错过了什么,请随时编辑。

答案 5 :(得分:1)

c#不提供析构函数,所以你的问题有点没有用?

答案 6 :(得分:1)

我不确定Destructors和Finalizers之间的推理,但Dispose存在,以便您可以在垃圾收集器到达对象之前释放资源(可能永远不会!)

答案 7 :(得分:1)

等待终结器运行以释放资源的问题在于它不能以可预测的确定性方式运行。 GC准备好销毁对象时调用终结器,这可以在应用程序中的任何时候发生。

这就是你要处置的原因。它允许您在已知的时间点确定性地释放资源,确保释放资源。否则你无法控制终结器的运行时间。

答案 8 :(得分:1)

Finalize无法访问托管对象,因此应在Dispose中处理托管对象。这两者之间的区别在于您应该使用Dispose for managed和Finalize for unmanaged。您的析构函数应该调用您的Dispose,以便您正确清除内存。看看以下文章:

Code Project - IDisposable: What Your Mother Never Told You About Resource Deallocation Gil Fink - Memory Leak And The IDisposable Pattern