C#/CLI:如果在析构函数中使用Dispose(),则析构函数不会被调用

5

我有一个名为“CTransferManaged”的C++/CLI类,其中实现了终结器和析构函数:

CTransferManaged::~CTransferManaged()
{
    this->!CTransferManaged();
}
CTransferManaged::!CTransferManaged()
{
    //Clean up resources... 
}

这个类被包装在一个名为“CTransfer”的C#类中,该类包含一个类型为CTransferManaged的对象m_transfer。

如果这个类的析构函数只清除了对对象m_transfer的引用,我可以看到析构函数被调用(断点被命中):

~CTransfer()
{
    m_transfer = null; //breakpoint on this line
}

如果我调用m_transfer对象的Dispose()函数而不改变任何其他东西,析构函数就不再被调用(断点不再被命中)。有什么猜测原因吗?
~CTransfer()
{
    m_transfer.Dispose(); //breakpoint on this line
    m_transfer = null;
}

我希望手动调用Dispose(),因为我发现如果不手动调用Dispose(),C++/CLI对象(m_transfer)的资源无法正确清理。目前我不知道原因。
为什么当CTransfer(C#类)调用CTransferManaged::Dispose()(C++/CLI)时,它的析构函数不再被调用?

你的CTransfer类必须实现IDisposable接口,以便正确处理m_transfer成员的释放。看起来你已经做到了这一点。不要为CTransfer实现终结器。将成员设置为null没有任何有用的效果,并调用其Dispose()方法是错误的。 - Hans Passant
@HansPassant: 为什么在CTransfer的终结器中调用成员对象(m_transfer)的Dispose()方法是错误的?正如我所提到的,当我不对其调用Dispose()时,我发现m_transfer的资源没有被正确清理(是的,我知道这意味着它有问题...) - obruend
2个回答

0

C# 析构函数只能被垃圾回收器调用,不能直接调用。

Dispose 方法是 IDisposalbe 接口的一部分,应该手动调用,这不会触发析构函数。

与其依赖析构函数,不如实现 iDisposabe 接口并直接调用 Dispose 方法,在其中放置您的代码。

如果 Resource 类包含需要处理的嵌入式对象,则需要使用 C# 中的此模式:

// C#
public class Resource : IDisposable
{
   private EmbeddedResource embedded;

   public Resource()
   {
      Console.WriteLine("allocating resource");
      embedded = new EmbeddedResource();
   }

   ~Resource()
   {
      Dispose(false);
   }

   protected virtual void Dispose(bool disposing)
   {
      Console.WriteLine("release resource");
      if (disposing)
      {
         embedded.Dispose();
      }
   }

   public void Dispose()
   {
      Dispose(true);
   }
}

请注意,我不想手动Dispose() C#对象(类CTransfer)。我只想使用其成员m_transfer(类CTransferManaged)的手动清理,因为否则它的资源不会被正确清理。 - obruend

0
典型的释放和终结模式是这样的:当您调用Dispose时,应该抑制终结器。Dispose旨在在执行时立即清除资源,而终结器旨在在垃圾收集器收集类时清除资源...两者都旨在做同样的事情(释放非托管资源),因此如果您调用Dispose,则调用终结器将是多余和不必要的,并且还会导致对象比需要更长时间地存活,因为它首先被放置在终结器队列中,然后才被收集和销毁。这会导致高访问应用程序上的内存管理不佳,这些应用程序创建和处理许多对象。因此,C#中的大多数Dispose方法将调用:
GC.SuppressFinalize(this);

这告诉垃圾回收器不执行 finalizer。这种模式被广泛使用,很可能也在您的非托管类中使用。这可能是为什么 Dispose 调用会消除析构函数的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接