什么时候处理方法不被调用?

时间:2009-09-10 11:35:15

标签: c# garbage-collection dispose idisposable finalizer

前几天我正在阅读this文章,并想知道为什么有一个Finalizer以及Dispose方法。我在SO上阅读了here,了解为什么要将Dispose添加到Finalizer中。我的好奇心是,什么时候会调用终结器本身的Dispose方法呢?是否有代码示例,或者它是基于软件运行的系统上发生的事情?如果是这样,可能会发生GC没有运行Dispose方法的情况。

5 个答案:

答案 0 :(得分:10)

此处终结器的目的仅仅是针对内存泄漏的安全预防措施(如果您明确调用Dispose)。这也意味着如果您希望在程序关闭时释放资源,则不必处置对象,因为GC将被强制完成并收集所有对象。

作为一个相关点,当从终结者处这样做时,以稍微不同的方式处理对象是很重要的。

~MyClass()
{
    Dispose(false);
}

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

protected void Dispose(disposing)
{
    if (!this.disposed)
    {
        if (disposing)
        {
            // Dispose managed resources here.
        }
        // Dispose unmanaged resources here.
    }
    this.disposed = true;
}

想要在终结器中配置托管资源的原因是您实际上会在这样做时创建对它们的强引用,这可能会阻止GC正确地执行它并且收集它们。当然,应该始终明确地关闭/处理非托管资源(例如Win32句柄等),因为CLR不了解它们。

答案 1 :(得分:4)

这主要是为了保护自己。你无法决定你班级的最终用户会做什么。通过提供除Dispose方法之外的终结器,GC将“处理”您的对象,适当地释放您的资源,即使用户忘记调用Dispose()或误用您的类。

答案 2 :(得分:2)

当对象被垃圾收集时调用Finalizer。需要明确调用Dispose。在下面的代码中,将调用终结器,但不会调用Dispose方法。

class Foo : IDisposable
{
  public void Dispose()
  {
    Console.WriteLine("Disposed");
  }

  ~Foo()
  {
    Console.WriteLine("Finalized");
  }
}

...

public void Go()
{
  Foo foo = new Foo();
}

答案 3 :(得分:1)

必须通过调用Dispose()或将对象放在using语句中显式调用dispose方法。 GC将始终调用终结器,因此如果在处理对象之前需要执行某些操作,则应至少检查以确保对象中的所有内容都已清除。

如果可能的话,你想避免清理终结器中的对象,因为与手头处理它们相比会产生额外的工作(比如调用dispose),但是如果有对象,你应该总是至少检查终结器需要删除。

答案 4 :(得分:0)

尚未提及的一个重要但微妙的注意事项:Dispose的一个很少考虑的目的是防止物体过早地被清理。必须仔细编写带有终结器的对象,以免终结器早于运行。终结器不能在对象(*)的最后一次方法调用开始之前运行,但是如果在最后一次方法调用期间它可能会在期间运行方法完成。正确处理对象的代码在调用Dispose之前不能放弃该对象,因此没有终结器会对正确使用Dispose的代码造成严重破坏的危险。另一方面,如果使用对象的最后一个方法使用了在最后一次使用对象引用本身后将在终结器中清理的实体,则垃圾收集器可以在对象上调用Finalize并清理正在使用的实体。解决方法是确保任何使用将被终结器清理的实体的调用方法必须在某个时候通过使用“this”的方法调用来跟踪。 GC.KeepAlive(this)是一个很好的方法。

(*)扩展为不对对象执行任何操作的内联代码的非虚方法可以免除此规则,但Dispose通常是或调用虚方法。