2010-07-13 38 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; 
    } 
}