2010-07-13 27 views
13

Tôi có một lớp học sử dụng các tệp tạm thời (Path.GetTempFileName()) trong khi nó đang hoạt động. Tôi muốn đảm bảo rằng các tệp này không còn trên ổ cứng của người dùng chiếm dung lượng sau khi chương trình của tôi bị đóng. Ngay bây giờ lớp của tôi có phương thức Close() để kiểm tra xem có bất kỳ tệp tạm nào được lớp đó sử dụng hay không và xóa chúng.Dispose() hoặc Finalize() có được sử dụng để xóa các tệp tạm thời không?

Thay vào đó, bạn nên đặt mã này vào các phương thức Vứt bỏ() hoặc Kết thúc()?

+0

Hãy cẩn thận với những kẻ đánh bóng. Có thể hệ thống sẽ khóa một tệp tạm thời vì bất kỳ lý do gì và việc xóa của bạn sẽ không thành công trong quá trình vứt bỏ. Bạn vẫn sẽ cần phải loại bỏ chúng ở một số điểm ít nhất bạn có một loạt các zombie trên hệ thống tập tin của bạn. – diadem

Trả lời

39

Tốt hơn là tạo tệp bằng FileOptions.DeleteOnClose. Điều này sẽ đảm bảo rằng hệ điều hành bắt buộc xóa tệp khi quá trình của bạn thoát (ngay cả trong trường hợp hủy bỏ thô lỗ). Tất nhiên, bạn vẫn muốn tự mình đóng/xóa tệp khi hoàn thành, nhưng điều này cung cấp một backstop tốt đẹp để đảm bảo rằng bạn không cho phép các tệp được ngồi xung quanh mãi mãi

+4

+1 cho tính năng hiếm khi được sử dụng. –

+5

Ví dụ: 'using (FileStream fs = File.Create (Path.GetTempFileName(), Int16.MaxValue, FileOptions.DeleteOnClose)) {// Sử dụng tệp tạm thời} // Tệp sẽ bị xóa ở đây' –

0

Tuyệt đối. Bằng cách này bạn có thể đảm bảo dọn dẹp với các ngoại lệ hiện tại.

+3

Giả sử bạn sử dụng C#, đặt lớp của bạn trong một khối mã using(). Khi cá thể của lớp của bạn nằm ngoài phạm vi (trả về bình thường hoặc ngoại lệ), Dispose() của bạn sẽ được gọi. – GregC

+3

Quy tắc ngón tay cái là triển khai IDisposable cho tất cả các lớp sở hữu tham chiếu đến đối tượng triển khai IDisposable. Có một quy tắc FxCop cho điều đó. – GregC

0

Bạn chắc chắn nên sử dụng Dispose để xóa tài nguyên, nhưng đảm bảo bạn triển khai giao diện IDisposable. Bạn không muốn chỉ thêm phương thức có tên là Dispose.

5

Tệp là tài nguyên không được quản lý và bạn triển khai IDisposable để xóa các tài nguyên không được quản lý mà các lớp của bạn phụ thuộc.

Tôi đã triển khai các lớp tương tự, mặc dù không bao giờ có trong mã sản xuất.

Tuy nhiên, tôi hiểu tính linh hoạt của bạn về điều này - tương tác của người dùng với các tệp bên ngoài ứng dụng của bạn có thể làm hỏng mọi thứ và gây ra sự cố trong khi xử lý. Tuy nhiên, điều này cũng giống nhau đối với bất kỳ tệp nào được tạo/xóa bởi một ứng dụng, bất kể nó có được dọn dẹp bởi phương thức Dispose() hay không.

Tôi phải nói rằng việc triển khai IDisposable sẽ là một lựa chọn hợp lý.

1

Một cách hay là được đề xuất bởi David M. Kean về mục MSDN trên Path.GetTempFileName. Ông tạo ra một lớp wrapper thực hiện IDisposable đó sẽ tự động loại bỏ các tập tin:

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; 
    } 
} 

Sử dụng lớp mới rất dễ dàng, chỉ cần gõ như sau:

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

Nếu bạn quyết định, sau khi xây dựng một TemporaryFile, mà bạn muốn ngăn chặn nó bị xóa, chỉ cần đặt thuộc tính TemporaryFile.Keep thành true:

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

Tôi luôn tạo các lớp học trỏ đến các tệp tạm thời IDisposable và thường thực hiện trình hoàn tất gọi phương thức vứt bỏ của tôi ở đó. Điều này có vẻ là mô hình được đề xuất bởi IDisposable MSDN page.

đang liên quan dưới đây:

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

Không sử dụng finalizers. Tìm hiểu về chúng, nhưng hãy tránh xa (trừ khi bạn đang thực hiện các công cụ mã được quản lý nâng cao). https://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/ – PMBjornerud

8

tôi sẽ làm cả hai; làm cho lớp học dùng một lần, và có người quyết định dọn dẹp nó.Có một mẫu tiêu chuẩn để làm như vậy một cách an toàn và hiệu quả: sử dụng nó thay vì cố gắng suy ra cho chính mình mô hình phù hợp là gì. Rất dễ mắc sai lầm. Đọc cẩn thận:

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

Lưu ý rằng bạn đã có được thực sự thực sự cẩn thận khi viết một finalizer. Khi finalizer chạy, nhiều giả định bình thường của bạn là sai:

  • Có tất cả các loại tiềm năng cho điều kiện chủng tộc hoặc bế tắc bởi vì bạn không còn trên các chủ đề chính, bạn đang ở trên thread finalizer.

  • Trong mã thông thường, nếu bạn đang chạy mã bên trong một đối tượng thì bạn biết rằng tất cả những thứ mà đối tượng đề cập đến vẫn còn sống. Trong một finalizer, tất cả những điều đối tượng đề cập đến có thể vừa được hoàn thành! Các finalizers của các đối tượng chết có thể chạy theo thứ tự bất kỳ, bao gồm các đối tượng "con" được hoàn thành trước các đối tượng "cha mẹ".

  • Trong mã thông thường, việc gán tham chiếu đến đối tượng vào trường tĩnh có thể hoàn toàn hợp lý. Trong trình hoàn thiện, tham chiếu bạn đang gán có thể là đối tượng đã chết đã chết và do đó nhiệm vụ mang đến một vật thể chết trở lại cuộc sống. (Bởi vì các đối tượng được gọi bởi các trường tĩnh luôn luôn sống.) Đó là một trạng thái kỳ lạ cực kỳ để ở trong và không có gì dễ chịu xảy ra nếu bạn làm.

  • Và cứ tiếp tục như vậy. Hãy cẩn thận. Bạn được mong đợi là hoàn toàn hiểu hoạt động của bộ thu gom rác nếu bạn viết một trình tổng hợp không tầm thường.

1

Nếu bạn muốn sử dụng lại các tệp tạm thời của mình, ví dụ: mở \ close \ read \ write \ etc, sau đó xóa chúng ở mức tải xuống AppDomain có thể hữu ích.

Điều này có thể được sử dụng kết hợp với việc đặt các tệp tạm thời trong thư mục con nổi tiếng của vị trí tạm thời và đảm bảo rằng thư mục bị xóa khi khởi động ứng dụng để đảm bảo việc đóng cửa không được quan tâm.

Ví dụ cơ bản về kỹ thuật (với xử lý ngoại lệ được xóa xung quanh xóa trong ngắn gọn). Tôi sử dụng kỹ thuật này trong các bài kiểm tra đơn vị dựa trên tệp mà nó có ý nghĩa và hữu ích.

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; 
    } 
} 
Các vấn đề liên quan