2010-07-13 62 views
13

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

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

+0

请注意散兵队员。系统可能会因为任何原因锁定临时文件,并且在处理过程中您的删除操作将失败。你至少还需要移除这些文件系统上的僵尸。 – diadem 2010-07-13 20:13:29

回答

39

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

+4

对于一个很好用的功能来说+1。 – 2010-07-13 20:12:45

+5

示例:使用(FileStream fs = File.Create(Path.GetTempFileName(),Int16.MaxValue,FileOptions.DeleteOnClose)){//使用临时文件} //文件将在此处被删除 – 2010-07-13 20:25:36

0

绝对。通过这种方式,您可以确保清理存在异常。

+3

假设你使用C#,把你的类放在using()代码块中。当你的类的实例超出范围(正常返回或异常)时,你的Dispose()将被调用。 – GregC 2010-07-13 20:06:38

+3

经验法则是为拥有对实现IDisposable的对象的引用的所有类实现IDisposable。有一个FxCop规则。 – GregC 2010-07-13 20:07:55

0

你一定要用Dispose来清理资源,但要确保你实现了IDisposable接口。您不想仅添加名为Dispose的方法。

5

文件是非托管资源,并且您实现了IDisposable来清理您的类依赖的非托管资源。

我已经实现了类似的类,虽然从来没有在生产代码。

但是,我理解你对此的假设 - 用户与应用程序之外的文件进行交互可能会导致事件发生并导致处理过程中出现问题。但是,对于由应用程序创建/删除的任何文件而言,这是相同的,无论它是否由Dispose()方法整理或不整理。我不得不说,实施IDisposable将是一个合理的选择。

1

一个好方法是由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; 
} 
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); 
} 
+0

请勿使用终结器。了解它们,但远离(除非你正在进行托管代码的东西)。 https://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/ – PMBjornerud 2016-08-11 11:27:14

8

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

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

注写一个终结的时候,你一定是在真的很细心。当终结运行,你的许多正常的假设是错误的:

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

  • 在普通代码中,如果你在一个对象内运行代码,那么你知道对象引用的所有东西都是活着的。在终结者中,对象引用的所有东西可能刚刚完成!死对象的终结器可以以任何顺序运行,包括在“父”对象之前完成的“子”对象。

  • 在常规代码中,将对象的引用分配给静态字段可能是非常明智的。在终结器中,您正在分配的引用可能是已经死了的对象,因此赋值会将死对象带回生命中。 (因为由静态字段引用的对象总是处于活动状态)。这是一个很奇怪的状态,如果你这样做的话就没有什么愉快的事情发生。

  • 依此类推。 小心点。如果你写了一个非平凡的终结器,你应该知道完全了解垃圾回收器的操作。

1

如果您希望重新使用临时文件,例如打开\关闭\读\写\等,然后清除他们在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; 
    } 
}