Dispose()或Finalize()是否应该用于删除临时文件?

时间:2010-07-13 19:59:04

标签: c# .net idisposable temporary-files

我有一个在激活时使用临时文件(Path.GetTempFileName())的类。我想确保在程序关闭后这些文件不会留在用户的硬盘驱动器上占用空间。现在我的类有一个Close()方法,它检查该类使用的任何临时文件是否仍然存在并删除它们。

将此代码放入Dispose()或Finalize()方法会更有意义吗?

8 个答案:

答案 0 :(得分:43)

更好的方法是使用FileOptions.DeleteOnClose创建文件。这将确保操作系统在您的进程退出时强制删除文件(即使在粗暴中止的情况下)。当然,你完成后仍然需要自己关闭/删除文件,但这提供了一个很好的支持,以确保你不允许文件永远存在

答案 1 :(得分:10)

我会两个都做;让这个级别一次性,并让终结器清理它。安全有效地采用标准模式:使用,而不是试图自己推断出正确的模式。这很容易出错。仔细阅读

http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

请注意,在编写终结器时,您必须非常小心。当终结者运行时,你的许多正常假设是错误的:

  • 竞争条件或死锁有各种各样的潜力,因为你不再在主线程上,你在终结者线程上。

  • 在常规代码中,如果您在对象中运行代码,那么您就知道对象引用的所有内容都是活动的。在终结器中,对象引用的所有内容可能刚刚完成!死对象的终结器可以按任何顺序运行,包括在“父”对象之前完成的“子”对象。

  • 在常规代码中,将对象的引用分配给静态字段可能是完全合理的。在终结器中,您分配的引用可能是已经死亡的对象,因此该赋值会使死对象恢复生命。 (因为静态字段引用的对象总是处于活动状态。)这是一个非常奇怪的状态,如果你这样做,就不会发生任何令人愉快的事情。

  • 等等。 小心。如果你写了一个非平凡的终结器,你应该完全了解垃圾收集器的操作。

答案 2 :(得分:6)

文件是非托管资源,您实现IDisposable以清理您的类所依赖的非托管资源。

我已经实现了类似的类,但从未在生产代码中实现。

但是,我理解你对此的暂时性 - 用户与应用程序之外的文件进行交互可能会搞砸,并在处置过程中造成问题。但是,对于应用程序创建/删除的任何文件,无论是否通过Dispose()方法进行整理,都是相同的。

我不得不说实施IDisposable是一个合理的选择。

答案 3 :(得分:2)

David M. Kean在Path.GetTempFileName上的MSDN条目中提出了一个很好的方法。他创建了一个实现IDisposable的包装类,它将自动删除文件:

public class TemporaryFile : IDisposable
{
    private bool _isDisposed;

    public bool Keep { get; set; }
    public string Path { get; private set; }

    public TemporaryFile() : this(false)
    {
    }

    public TemporaryFile(bool shortLived)
    {
        this.Path = CreateTemporaryFile(shortLived);
    }

    ~TemporaryFile()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            if (!this.Keep)
            {
                TryDelete();   
            }
        }
    }

    private void TryDelete()
    {
        try
        {
            File.Delete(this.Path);
        }
        catch (IOException)
        {
        }
        catch (UnauthorizedAccessException)
        {
        }
    }

    public static string CreateTemporaryFile(bool shortLived)
    {
        string temporaryFile = System.IO.Path.GetTempFileName();

        if (shortLived)
        { 
            // Set the temporary attribute, meaning the file will live 
            // in memory and will not be written to disk 
            //
            File.SetAttributes(temporaryFile, 
                File.GetAttributes(temporaryFile) | FileAttributes.Temporary);
        }

        return temporaryFile;
    }
}
  

使用新类很简单,只需输入以下内容:

using (TemporaryFile temporaryFile = new TemporaryFile())
{
    // Use temporary file
}
  

如果您在构建TemporaryFile之后决定要阻止它被删除,只需将TemporaryFile.Keep属性设置为true:

using (TemporaryFile temporaryFile = new TemporaryFile()) 
{ 
    temporaryFile.Keep = true; 
}

答案 4 :(得分:1)

我总是让我的类指向临时文件IDisposable,并且通常实现一个在那里调用我的dispose方法的终结器。这似乎是IDisposable MSDN page建议的范例。

以下相关代码:

public void Dispose()
{
    Dispose(true);
    // This object will be cleaned up by the Dispose method.
    // Therefore, you should call GC.SupressFinalize to
    // take this object off the finalization queue
    // and prevent finalization code for this object
    // from executing a second time.
    GC.SuppressFinalize(this);
}

// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
private void Dispose(bool disposing)
{
    // Check to see if Dispose has already been called.
    if(!this.disposed)
    {
        // If disposing equals true, dispose all managed
        // and unmanaged resources.
        if(disposing)
        {
            // Dispose managed resources.

        }

        // Call the appropriate methods to clean up
        // unmanaged resources here.
        // If disposing is false,
        // only the following code is executed.


        // Note disposing has been done.
        disposed = true;

    }
}



// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
    // Do not re-create Dispose clean-up code here.
    // Calling Dispose(false) is optimal in terms of
    // readability and maintainability.
    Dispose(false);
}

答案 5 :(得分:1)

如果您想重新使用临时文件,例如open \ close \ read \ write \ etc,然后在AppDomain卸载级别清除它们会很有用。

这可以与将temp文件放在临时位置的一个众所周知的子目录中并确保在应用程序启动时删除该目录以确保处理不干净的关闭一起使用。

该技术的一个基本示例(为简洁起见,删除了删除异常处理)。我在基于文件的单元测试中使用这种技术,它有意义且有用。

public static class TempFileManager
{
    private static readonly List<FileInfo> TempFiles = new List<FileInfo>();
    private static readonly object SyncObj = new object();

    static TempFileManager()
    {
        AppDomain.CurrentDomain.DomainUnload += CurrentDomainDomainUnload;
    }

    private static void CurrentDomainDomainUnload(object sender, EventArgs e)
    {
        TempFiles.FindAll(file => File.Exists(file.FullName)).ForEach(file => file.Delete());
    }

    public static FileInfo CreateTempFile(bool autoDelete)
    {
        FileInfo tempFile = new FileInfo(Path.GetTempFileName());

        if (autoDelete)
        {
            lock (SyncObj)
            {
                TempFiles.Add(tempFile);
            }
        }

        return tempFile;
    }
}

答案 6 :(得分:0)

绝对。这样,您可以确保在出现异常的情况下进行清理。

答案 7 :(得分:0)

您绝对应该使用Dispose来清理资源,但请确保实现IDisposable接口。您不想只添加名为Dispose的方法。