什么时候处置?为什么?

我问了一个关于这个方法的问题 :

// Save an object out to the disk public static void SerializeObject(this T toSerialize, String filename) { XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); TextWriter textWriter = new StreamWriter(filename); xmlSerializer.Serialize(textWriter, toSerialize); textWriter.Close(); } 

在回复中,我将此作为补充评论:

确保始终处理一次性资源,例如流和文本阅读器和编写器。 在您的SerializeObject方法中似乎不是这种情况。

所以,我可以说,对于那些编写C#一年或两年的人来说,这似乎是非常蹩脚的,但为什么我必须处理它呢?

是看到testWriter有一个dispose方法,但是不应该把垃圾收集照顾testWriter吗? 我从Delphi来到C#。 在Delphi中我不得不清理所有东西,所以这不是我想要变懒的情况。 我刚刚被告知,如果你强行释放你的对象占用的内存,那么它可能会导致坏事。 有人告诉我“让垃圾收集器去做”。

  1. 那么,为什么我需要调用dispose? (我的猜测是因为textWriter击中了磁盘。)
  2. 是否有我需要注意的对象列表? (或者知道何时需要调用dispose的简单方法?)

你是正确的,对于正确编写的代码,GC最终将清理本机资源。 该对象将具有终结器,并且在最终确定期间将释放必要的本机资源。

然而,当这种情况发生时非常不确定。 此外,它有点倒退,因为您正在使用旨在处理托管内存的GC作为管理本机资源的方法。 这导致了有趣的案例,并且可能导致本机资源比预期更长时间保持活跃,从而导致出现这种情况

  • 文件在不再使用后很长时间打开
  • 资源句柄可能会耗尽,因为GC没有看到足够的内存压力来强制收集并因此运行终结器

using / dispose模式为本机资源的清理添加了确定性并消除了这些问题。

这里的经验法则非常简单:总是在实现IDisposable (不是所有对象)的对象上调用Dispose() )。 您不会总是知道对象必须实现Dispose的原因,但您应该假设它存在的原因。

确保执行此操作的最简单方法是using

 using (TextWriter tw = new StreamWriter(fileName)) { // your code here } 

这将在using块的末尾自动调用Dispose() (它与使用try / catch / finally和finally块中的Dispose()基本相同)。

有关Dispose如何与垃圾收集一起使用的更多信息, 请参见此处 。

垃圾收集器会释放所有资源,但是执行此操作的时间是未定义的。 Dispose方法提供了一种立即释放非托管资源的方法。

如果你知道你不会使用某种资源,你可以自己处理它; 你肯定会比垃圾收集器更快,并允许其他人使用该文件或你打开的更快。 最简单的方法是在使用中使用TextWriter或任何其他资源:

 using (TextWriter textWriter = new StreamWriter(filename)) { xmlSerializer.Serialize(textWriter, toSerialize); } 

这基本上确保TextWriter在最后处置。 无论如何,你不需要它。

实际上你已经在处理它了,因为textWriter.Close方法做到了。

 public virtual void Close() { this.Dispose(true); GC.SuppressFinalize(this); } 

所以你可以改变你的代码。 这个

 public static void SerializeObject(this T toSerialize, String filename) { TextWriter textWriter; try { XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); textWriter = new StreamWriter(filename); xmlSerializer.Serialize(textWriter, toSerialize); } finally { textWriter.Close(); } 

这与using()在其他答案中的作用非常相似。

不这样做的影响是,如果Serialize发生错误,那么在框架放弃文件锁定(当它处理fReachable队列时)之前会有一段时间。

我知道FxCop会告诉你何时实施IDisposable,但我认为除了查看Docs以及查看对象是否包含IDisposable(或intellisense)之外,还有任何简单的方法可以找出你需要调用Dispose的时间。

如果您正在使用本机资源(例如文件句柄),那么您应该尽快调用Dispose()来关闭它们,而不是在GC运行时(可能在更高的gc代中更晚)。 并且您希望关闭该文件,因为文件访问通常以某种方式锁定文件。

如果要打开资源(例如文件或打开数据库连接),则释放资源将释放其对资源的保留。 如果您不这样做,那么其他人可能无法连接到数据库或使用该文件。

作为一般经验法则….如果类实现了IDisposable接口,那么在完成它时应该调用Dispose()方法。 很可能有一个原因让他们把它变成一次性的:)

从TextWriter.Dispose文档:

注意在释放对TextWriter的最后一个引用之前, 始终调用Dispose。 否则,在垃圾收集器调用TextWriter对象的Finalize方法之前,不会释放它正在使用的资源。

从Object.Finalize文档:

在垃圾收集期间执行终结器的确切时间是未定义的。 除非调用Close方法或Dispose方法,否则不保证在任何特定时间释放资源。

Finalize方法可能无法运行完成,或者在以下特殊情况下可能根本不运行:

  • 另一个终结器无限期地阻塞(进入无限循环,尝试获得它永远无法获得的锁等等)。 由于运行时尝试将终结器运行完成,因此如果终结器无限期地阻塞,则可能不会调用其他终结器。

  • 该过程终止而不给运行时提供清理的机会。 在这种情况下,运行时的第一个进程终止通知是DLL_PROCESS_DETACH通知。

只有在可终结对象的数量继续减少时,运行时才会在关闭期间继续完成对象。

垃圾收集器确实会“照顾好”。 迟早。 当它在下一次垃圾收集之后调用终结器时。

调用Dispose可确保尽快释放资源(如文件句柄),从而使其可供系统中的其他进程重用。 大多数情况下,您不会注意到自己调用Dispose和让垃圾收集器处理它之间的区别。 但有时你会。 例如,创建大量HttpWebResponse对象的Web爬虫如果在使用后不进行处理,将很快耗尽连接。 (不是说我遇到了那个问题……不,不是我。)

调用Dispose而不是等待GC的原因通常是因为您正在使用要尽快释放的有限系统资源,以便其他进程或线程可以使用它。 对于具有IDisposable模式的对象, “using”构造是一种简单易读的方法,可确保调用Dispose。

通常,当您不再需要它们时,您必须处置它们。

在完成对象时不处理对象将意味着将阻止对其他处理/应用程序的访问。

一个程序员认为一个人不必担心处理事情,因为终结者会照顾他们就像一个司机认为人们不必担心避免碰撞,因为汽车有一个安全气囊。 是的,安全气囊会让事情更有生存能力,但……