20

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

将此代码放在 Dispose() 或 Finalize() 方法中是否更有意义?

4

8 回答 8

45

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

例子:

using (FileStream fs = File.Create(Path.GetTempFileName(), Int16.MaxValue, 
       FileOptions.DeleteOnClose)) 
{ 

    // Use temp file 

} // The file will be deleted here
于 2010-07-13T20:09:46.653 回答
13

我会两者兼而有之;使类一次性,并让终结器清理它。有一个安全有效的标准模式:使用它而不是试图自己推断正确的模式是什么。很容易出错。仔细阅读:

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

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

  • 由于您不再在主线程上,而是在终结器线程上,因此存在各种潜在的竞争条件或死锁。

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

  • 在常规代码中,将对对象的引用分配给静态字段可能是非常明智的。在终结器中,您分配的引用可能是对已经死的对象,因此分配使死对象恢复生机。(因为静态字段引用的对象总是活动的。)这是一种非常奇怪的状态,如果你这样做了,不会有任何愉快的事情发生。

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

于 2010-07-13T21:38:16.323 回答
6

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

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

但是,我理解您对此的试探性 - 用户与应用程序外部文件的交互可能会搞砸并在处理过程中导致问题。但是,对于应用程序创建/删除的任何文件,这都是相同的,无论它是否由 Dispose() 方法整理。

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

于 2010-07-13T20:07:09.853 回答
3

David M. Kean 在Path.GetTempFileName. 他创建了一个实现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; 
}
于 2010-07-13T20:23:02.190 回答
1

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

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

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

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;
    }
}
于 2010-07-13T23:15:29.477 回答
1

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

于 2010-07-13T20:06:56.083 回答
1

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

相关代码如下:

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);
}
于 2010-07-13T20:29:20.320 回答
1

绝对地。这样,您可以确保清理存在异常。

于 2010-07-13T20:05:22.077 回答