2010-10-15 42 views
8

Tôi biết điều này có vẻ ngớ ngẩn, nhưng tại sao mã sau chỉ hoạt động nếu tôi Đóng() tệp? Nếu tôi không đóng tệp, toàn bộ luồng sẽ không được ghi.Tại sao tôi phải Đóng() một tệp trong C#?

bước:

  1. Run mã này vào tải mẫu đơn.
  2. Đóng biểu mẫu bằng chuột khi nó được hiển thị.
  3. Chương trình chấm dứt.

Không nên để đối tượng tệp bị xóa hoặc đóng tự động khi nó nằm ngoài phạm vi? Tôi mới đến C#, nhưng tôi được sử dụng để thêm các cuộc gọi đến Close() trong C + + destructors.

// Notes: complete output is about 87KB. Without Close(), it's missing about 2KB at the end. 

// Convert to png and then convert that into a base64 encoded string. 
string b64img = ImageToBase64(img, ImageFormat.Png); 
// Save the base64 image to a text file for more testing and external validation. 
StreamWriter outfile = new StreamWriter("../../file.txt"); 
outfile.Write(b64img); 
// If we don't close the file, windows will not write it all to disk. No idea why 
// that would be. 
outfile.Close(); 
+5

Bạn có thể tìm thấy các cuộc thảo luận tại http://blogs.msdn.com/b/oldnewthing/archive/ 2010/08/10/10048150.aspx hữu ích trong việc cung cấp giải thích và biện minh cho sự khác biệt giữa C# và C++. Nói chung, tôi sẽ so sánh C++ destructors với C# 'IDisposable' và' using {} '. Và dĩ nhiên, 'using' an toàn hơn gọi' Close' hoặc 'Dispose' một cách rõ ràng, vì nó kết thúc cuộc gọi trong khối' try-finally' để đảm bảo đối tượng được xử lý. – Brian

+0

Cảm ơn tất cả vì sự giúp đỡ rõ ràng và nhanh chóng. – Harvey

Trả lời

21

C# không có dọn dẹp xác định tự động. Bạn phải chắc chắn để gọi chức năng dọn dẹp nếu bạn muốn kiểm soát khi nó chạy. Khối using là cách phổ biến nhất để thực hiện việc này.

Nếu bạn không thực hiện cuộc gọi dọn dẹp, thì việc dọn dẹp sẽ xảy ra khi bộ thu gom rác quyết định bộ nhớ cần thiết cho một thứ khác, có thể sau một thời gian rất dài.

using (StreamWriter outfile = new StreamWriter("../../file.txt")) { 
    outfile.Write(b64img); 
} // everything is ok, the using block calls Dispose which closes the file 

EDIT: Khi Harvey chỉ ra, trong khi dọn dẹp sẽ được thực hiện khi đảm bảo thành công. Để tránh các vấn đề với tham chiếu vòng tròn, thời gian chạy không cố gắng hoàn thành các đối tượng theo thứ tự "đúng", do đó, FileStream thực sự có thể đã chết trước khi trình hoàn thiện StreamWriter chạy và cố gắng xóa đầu ra được lưu vào bộ đệm.

Nếu bạn xử lý các đối tượng cần dọn dẹp, hãy thực hiện rõ ràng, với using (để sử dụng tại địa phương) hoặc gọi IDisposable.Dispose (đối với các đối tượng tồn tại lâu dài chẳng hạn như tham chiếu của thành viên nhóm).

+1

Như một bên, bất cứ khi nào bạn nhận thấy một đối tượng thực hiện 'IDisposable', bạn nên sử dụng' using', trừ khi bạn không muốn đối tượng được làm sạch ngay lập tức (ví dụ nếu nó là biến thành viên, bạn có thể muốn 'Vứt bỏ' nó sau này). – Brian

+0

hãy tưởng tượng C++ phải dọn sạch mọi thứ ... đáng sợ – Spooks

+0

Tôi đoán điều khiến tôi mất cảnh giác là chức năng dọn dẹp là ** không bao giờ ** gọi cho tôi trừ khi tôi đóng. Có vẻ như khi tôi chấm dứt chương trình, thay vì chạy dọn dẹp trên bất kỳ đối tượng nào cần phải xử lý, nó chỉ chấm dứt mà không cần thu gom rác thải. – Harvey

8

Vì ghi() được đệm và bộ đệm được xóa hoàn toàn bằng Close().

+1

Hoặc bằng cách gọi hàm Flush() rõ ràng. –

+2

Việc xử lý tệp gốc cần được đóng là IMO quan trọng hơn. Đặc biệt là vì điều này có thể khóa tập tin cho đến khi finalizer cuối cùng chạy. – CodesInChaos

1

Bộ nhớ cache của hệ điều hành viết để chặn các thiết bị để cho phép hệ điều hành có hiệu suất tốt hơn. Bạn buộc ghi bằng cách xóa bộ đệm sau khi ghi thiết lập bộ ghi dòng để tự động điền.

+0

Vẫn không đóng tệp và hầu hết các chương trình khác không thể mở tệp cho đến khi chương trình đầu tiên đóng. –

+1

Yea quan điểm của tôi là ép buộc bạn không phải đóng – rerun

3

Luồng là các đối tượng "quản lý" hoặc "xử lý" các tài nguyên không thu thập rác thải. Do đó, chúng (Streams) thực hiện giao diện IDisposable, khi được sử dụng với 'using' sẽ đảm bảo rằng các tài nguyên không thu thập rác được dọn sạch. hãy thử điều này:

using (StreamWriter outfile = new StreamWriter("../../file.txt")) 
{ 
    outfile.Write(b64img); 
} 

Nếu không có #Đóng, bạn không thể chắc chắn khi xử lý tệp cơ bản sẽ được đóng đúng cách. Đôi khi, điều này có thể là lúc tắt ứng dụng.

+0

Cảm ơn, điều này giúp làm sáng tỏ một số nhầm lẫn. Trong trường hợp của tôi, xử lý tập tin đã được ** không bao giờ ** đóng ngay cả khi tắt ứng dụng. (Đóng trong ý nghĩa rằng một rõ ràng Close() sẽ tuôn ra) – Harvey

+0

bạn được chào đón. – DevSolo

-2

Bởi vì các nhà thiết kế C# đã nhân bản Java chứ không phải C++ mặc dù tên.

Theo ý kiến ​​của tôi, họ thực sự đã bỏ lỡ chiếc thuyền. Phá hủy kiểu C++ khi thoát khỏi phạm vi sẽ là tốt hơn rất nhiều.

Nó thậm chí sẽ không phải giải phóng bộ nhớ để được tốt hơn, chỉ cần tự động chạy finalizer hoặc phương pháp IDisposable.

+2

-1: Việc khiếu nại về ngôn ngữ bị thiếu sót không thực sự hữu ích, đặc biệt là phàn nàn như "C# là một bản sao của Java và đó là lý do tại sao nó hoạt động như Java." Đây là một quyết định thiết kế có chủ ý, và nó liên quan đến cách thức hoạt động của bộ sưu tập rác trong C#. Xem http://blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048150.aspx để biết giải thích tại sao việc xử lý tự động không hoạt động. – Brian

+1

Tôi không phải là người đã xuống hạng, nhưng tại đại diện 6k, bạn nên biết cách để lại nhận xét ngay bây giờ. Đây không phải là câu trả lời. BTW những gì bạn muốn tồn tại, nó được gọi là C++/CLI, .NET hỗ trợ với RAII và các cuộc gọi tự động đến destructor ở cuối phạm vi. –

+1

lớp C {Stream s; void M() {S s2 = OpenAStream(); this.s = s2; }} - ý tưởng của bạn là luồng sẽ được xử lý tự động khi s2 nằm ngoài phạm vi, mặc dù this.s vẫn có tham chiếu? Bạn không nghĩ rằng sẽ gây ngạc nhiên cho mọi người? –

2

Vì bạn đang sử dụng trình viết luồng và không xóa bộ đệm cho đến khi bạn viết Close(). Bạn có thể chỉ định rằng bạn muốn người viết tuôn ra mọi lúc bạn gọi bằng cách đặt thuộc tính AutoFlush của người viết luồng thành true.

Xem tài liệu. http://msdn.microsoft.com/en-us/library/system.io.streamwriter.aspx

Nếu bạn muốn viết vào một tập tin mà không "đóng cửa", tôi sẽ sử dụng:

System.IO.File 
Các vấn đề liên quan