Liệu xử lý streamreader đóng luồng?


166

Tôi đang gửi một luồng đến các phương thức để viết và trong các phương thức đó tôi đang sử dụng trình đọc / ghi nhị phân. Khi người đọc / người viết bị loại bỏ, bởi usinghoặc chỉ khi nó không được tham chiếu, thì luồng có bị đóng không ??

Tôi sẽ gửi BinaryReader / Writer, nhưng tôi cũng đang sử dụng StreamReader (có lẽ tôi nên đi xung quanh đó. Tôi chỉ sử dụng điều đó cho GetLine và ReadLine). Điều này khá rắc rối nếu nó đóng luồng mỗi khi nhà văn / người đọc bị đóng.

Câu trả lời:


204

Vâng, StreamReader, StreamWriter, BinaryReaderBinaryWritertất cả gần / vứt bỏ suối tiềm ẩn của họ khi bạn gọi Disposevào chúng. Mặc dù vậy, họ không loại bỏ luồng nếu người đọc / người viết chỉ là rác được thu thập - bạn nên luôn luôn xử lý người đọc / người viết, tốt nhất là bằng một usingtuyên bố. (Trong thực tế, không có lớp nào trong số này có người hoàn thiện, cũng không nên có.)

Cá nhân tôi thích có một tuyên bố sử dụng cho luồng là tốt. Bạn có thể lồng usingcâu lệnh mà không cần niềng răng khá gọn gàng:

using (Stream stream = ...)
using (StreamReader reader = new StreamReader(stream, Encoding.Whatever))
{
}

Mặc dù usingcâu lệnh cho luồng có phần dư thừa (trừ khi nhà StreamReaderxây dựng ném ngoại lệ) Tôi coi đó là cách thực hành tốt nhất sau đó nếu bạn thoát khỏi StreamReadervà chỉ sử dụng luồng trực tiếp vào một ngày sau đó, bạn sẽ có quyền xử lý đúng ngữ nghĩa.


2
oh tốt, nó chỉ xảy ra khi gọi Vứt bỏ, không phải khi được cho là hoàn thiện.
Nefzen

1
@Nefzen: Đó là bởi vì không có gì đảm bảo thứ tự các đối tượng của bạn sẽ được hoàn thành. Nếu cả StreamReader và Stream bên dưới đều đủ điều kiện để hoàn thiện, thì GC có thể hoàn thành luồng trước - sau đó streamreader sẽ không có tham chiếu đến luồng. Vì lý do này, bạn chỉ có thể giải phóng các tài nguyên không được quản lý bên trong quyết toán (ví dụ: FileStream đóng xử lý tệp windows của nó trong quyết toán của nó). Ồ, và tất nhiên, nếu bạn không bao giờ loại bỏ, luồng cuối cùng vẫn sẽ được thu thập (và tệp bị đóng). Đó chỉ là một thực tế rất xấu để không xử lý một luồng.
JMarsch

13
Việc lồng nhau này làm cho bộ phân tích mã VS phàn nàn: Có CA2202 : Microsoft.Usage : Object 'stream' can be disposed more than once in method '...'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.nên bỏ qua điều đó không? Cho đến nay tôi không nhận được bất kỳ ngoại lệ nào ...
HB

15
@HB: Sẽ an toàn khi bỏ qua nó trong trường hợp này. Hoặc bạn chỉ có thể tạo luồng trong lệnh gọi hàm tạo StreamReader. Cảnh báo có vẻ không có thật với tôi, vì các tài liệu IDisposable.Disposenói rõ: "Nếu phương thức Vứt bỏ của một đối tượng được gọi nhiều lần, đối tượng phải bỏ qua tất cả các cuộc gọi sau lần đầu tiên. Đối tượng không được ném ngoại lệ nếu phương thức Vứt bỏ của nó là được gọi nhiều lần. "
Jon Skeet

5
@JonSkeet: Trên thực tế có một trang cho điều này , bạn đã đúng, đây là không có thật :)
HB

45

Đây là một cái cũ, nhưng tôi muốn làm một cái gì đó tương tự ngày hôm nay và thấy rằng mọi thứ đã thay đổi. Kể từ .net 4.5, có một leaveOpenđối số:

public StreamReader( Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen )

Vấn đề duy nhất là nó không hoàn toàn rõ ràng những gì cần đặt cho các tham số khác. Đây là một số trợ giúp:

Từ trang msDN cho Trình tạo luồng StreamReader (Luồng):

Hàm tạo này khởi tạo mã hóa thành UTF8Encoding, thuộc tính BaseStream bằng tham số luồng và kích thước bộ đệm bên trong thành 1024 byte.

Điều đó chỉ để lại detectEncodingFromByteOrderMarksviệc đánh giá theo mã nguồntrue

public StreamReader(Stream stream)
        : this(stream, true) {
}

public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
        : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}

Sẽ thật tuyệt nếu một số trong các mặc định đó được phơi bày hoặc nếu các đối số là tùy chọn để chúng tôi có thể chỉ định các đối số mà chúng tôi muốn.


Thông tin rất hay! Chưa bao giờ nghe về thông số mới này và nó thực sự có rất nhiều ý nghĩa.
julealgon

3
Đối với những người lười biếng như tôi, câu trả lời ngắn để mở luồng sẽ như sau:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf

29

Vâng, nó làm. Bạn có thể xác minh điều này bằng cách xem xét việc thực hiện với Reflector.

protected override void Dispose(bool disposing)
{
    try
    {
        if ((this.Closable && disposing) && (this.stream != null))
        {
            this.stream.Close();
        }
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {    
            this.stream = null;    
            this.encoding = null;
            this.decoder = null;
            this.byteBuffer = null;
            this.charBuffer = null;
            this.charPos = 0;
            this.charLen = 0;
            base.Dispose(disposing);
        }
    }
}

13

Sáu năm muộn nhưng có lẽ điều này có thể giúp được ai đó.

StreamReader sẽ đóng kết nối khi nó được xử lý. Tuy nhiên, "sử dụng (Stream stream = ...) {...}" với StreamReader / StreamWriter có thể dẫn đến Stream bị xử lý hai lần: (1) khi đối tượng StreamReader bị loại bỏ (2) và khi Stream sử dụng khối đóng cửa Điều này dẫn đến cảnh báo CA2202 khi chạy phân tích mã của VS.

Một giải pháp khác, được lấy trực tiếp từ trang CA2202 , là sử dụng khối thử / cuối cùng. Thiết lập chính xác, điều này sẽ chỉ đóng kết nối một lần.

Gần cuối CA2202 , Microsoft khuyên bạn nên sử dụng như sau:

Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.OpenOrCreate);
    using (StreamWriter writer = new StreamWriter(stream))
    {
        stream = null;
        // Use the writer object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

thay vì...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}

2
Cảnh báo được thảo luận trong các ý kiến ​​của câu trả lời được chấp nhận là tốt. Jon Skeet cung cấp một số lời khuyên ở đó.
Marcin

Ngoài ra, hãy lưu ý rằng câu lệnh sử dụng thực sự được chuyển đổi thành một khối thử cuối cùng bởi trình biên dịch.
Jason Kelley

2

Đúng. Gọi Dispose () trên và IDis Dùng (mà "sử dụng" sẽ làm cho một đối tượng dọn sạch tất cả các tài nguyên của nó. Điều này bao gồm các luồng tuôn ra và đóng mô tả tập tin của họ.

Nếu trong trường hợp của bạn, bạn muốn chuyển nó sang các phương thức khác, thì bạn cần đảm bảo rằng các phương thức đó không thực hiện việc đọc / ghi của chúng trong một khối sử dụng.



-2

luồng được xử lý bằng cách "sử dụng" từ khóa hoặc gọi một cách rõ ràng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.