【问题标题】:Is there a safe way to use a managed field in destructor?有没有在析构函数中使用托管字段的安全方法?
【发布时间】:2012-10-05 11:31:10
【问题描述】:

我有一个类在构造函数中创建一个隐藏文件并保持其FileStream 处于打开状态,直到调用Dispose()。调用Dispose() 会关闭流并移除隐藏文件。

如果程序在没有调用Dispose() 的情况下关闭,我需要确保删除该文件。我想我可以检查它是否仍然存在并调用File.Delete(path) 如果它在析构函数中存在,但是在调用析构函数时已经可以收集保存文件路径的字符串字段,对吧?

有没有一种安全的方法可以从析构函数中访问该字段?
如果没有,我还有其他方法可以确保删除该文件吗?

【问题讨论】:

  • 只需实现(完整的)IDisposable 模式,并在Dispose(bool) 方法中进行清理。您需要的数据仍然可用。 msdn.microsoft.com/en-us/library/system.idisposable.aspx
  • @spender:您应该将此作为答案发布...
  • @spender:你是对的。我想如果disposing 为假,这意味着托管字段可能已经被收集,这就是析构函数调用Dispose(false) 的原因,这是我提出这个问题的原因之一。在 Henk 的回答下查看我的评论。
  • 确保您了解已收集和已处置之间的区别。它们在这里非常不同。
  • 请澄清问题:“没有调用 Dispose()”的场景到底是什么?

标签: c# .net file destructor managed


【解决方案1】:

但是保存文件路径的字符串字段在调用析构函数的时候已经可以收集了吧?

没有。该字符串仍然存在并且可以安全使用。

但您必须谨慎操作,如果文件仍处于打开状态,请先将其关闭。

如果程序在没有调用 Dispose() 的情况下关闭,我需要确保文件被删除

这是一个巨大的挑战。这里没有太多保证。确保客户端代码始终调用 Dispose()。

【讨论】:

  • 如果类中的字段都没有被收集,我可以检查它是否打开。但是如果它们没有被收集,那么在使用 dispose 模式时在析构函数中调用Dispose(false) 有什么意义呢?不是因为它们已经被收集了,而是因为它们很快就会被收集,我想?
  • Dispose() 不适用于字符串,它不是关于收集对象。这是关于处置(=内部清理)。您的成员字段不会指向收集的对象,但它们(文件流)可能已经被 Disposed。所以检查一下。
  • Dispose(false) 是因为 a) Dispose() 成员没有必要这样做,b) 这样做并不总是完全安全的。
【解决方案2】:

我相信您可以简单地覆盖您正在使用的类的Finalize() 方法。根据this MSDN article

析构函数隐式调用对象基类的 Finalize。因此,前面的析构函数代码被隐式翻译成如下代码:

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

在使用Finalize() 方法时,您还可以确保尚未“收集”任何字段,因此您可以安全地使用对象的所有字段。

【讨论】:

    【解决方案3】:

    如果您的进程将崩溃或被杀死,则不会调用 Dispose() 方法。您应该测试某个文件是否被其他进程锁定,而不是在文件存在时休息

    【讨论】:

    • 如果进程崩溃或被杀死,是否会调用析构函数?
    • 没有。当进程无法运行时,不调用析构函数。例如,如果抛出 StackOverflowException 或 OutOfMemoryException,则不会调用析构函数。如果有人杀死你的进程,析构函数也不会被调用。如果电脑进入睡眠状态然后直接关机。不调用析构函数。等等……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-04
    • 1970-01-01
    • 2021-12-03
    • 2011-03-27
    • 2016-05-26
    相关资源
    最近更新 更多