什么时候处置?为什么?

时间:2010-10-21 18:41:04

标签: c# dispose

我问question这个方法:

// Save an object out to the disk
public static void SerializeObject<T>(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方法,但是垃圾收集不应该处理吗?我从Delphi来到C#。在Delphi中我不得不清理所有东西,所以这不是我想要变懒的情况。我刚刚被告知,如果你强行释放你的对象占用的内存,那么它可能会导致坏事。有人告诉我“让垃圾收集者去做吧。”

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

12 个答案:

答案 0 :(得分:15)

这里的经验法则非常简单:始终在实现Dispose()的对象上调用IDisposable(并非所有对象都可以)。你不会总是知道对象必须实现Dispose的原因,但是你应该认为它是有原因的。

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

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

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

有关Dispose如何使用垃圾收集的更多信息,请see here

答案 1 :(得分:12)

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

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

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

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

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:4)

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

答案 4 :(得分:3)

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

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

因此您可以将代码更改为。这个

public static void SerializeObject<T>(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的时间。 / p>

答案 5 :(得分:2)

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

答案 6 :(得分:1)

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

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

答案 7 :(得分:1)

来自TextWriter.Dispose文档:

  

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

来自Object.Finalize文档:

  

终结者的确切时间   在垃圾收集期间执行   未定义。资源不是   保证在任何时候发布   具体时间,除非调用关闭   方法或Dispose方法。

  

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

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

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

  •   
     

运行时继续Finalize   关闭期间的对象只有在   可终结对象的数量   继续减少。

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

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

答案 11 :(得分:0)

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