การกำจัดสตรีมปิดสตรีมหรือไม่


166

ฉันกำลังส่งกระแสข้อมูลไปยังวิธีการเขียนบนและในวิธีการเหล่านั้นฉันกำลังใช้ตัวอ่านไบนารี / wrtier เมื่อผู้อ่าน / ผู้เขียนได้รับการกำจัดไม่ว่าจะโดยusingหรือเพียงแค่เมื่อไม่มีการอ้างอิงสตรีมก็ปิดเช่นกัน ??

ฉันจะส่ง BinaryReader / Writer แต่ฉันใช้ StreamReader ด้วย (บางทีฉันควรจะไปรอบ ๆ นั้นฉันแค่ใช้มันสำหรับ GetLine และ ReadLine) นี่เป็นปัญหาค่อนข้างมากหากปิดสตรีมทุกครั้งที่ผู้เขียน / ผู้อ่านปิด

คำตอบ:


204

ใช่StreamReader, StreamWriter, BinaryReaderและBinaryWriterปิด / ทิ้งลำธารพื้นฐานของพวกเขาเมื่อคุณเรียกDisposeพวกเขา พวกเขาจะไม่กำจัดกระแสหากผู้อ่าน / นักเขียนเป็นเพียงขยะที่เก็บรวบรวม - คุณควรกำจัดผู้อ่าน / นักเขียนเสมอควรมีusingคำสั่ง (อันที่จริงไม่มีคลาสเหล่านี้ที่มี finalizers และไม่ควรมี)

ส่วนตัวผมชอบที่จะมีการใช้คำสั่งสำหรับกระแสเช่นกัน คุณสามารถซ้อนusingคำสั่งโดยไม่ต้องจัดฟันอย่างเรียบร้อยมาก:

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

แม้ว่าusingคำสั่งสำหรับการสตรีมจะค่อนข้างซ้ำซ้อน (เว้นแต่ว่าตัวStreamReaderสร้างโยนข้อยกเว้น) ฉันคิดว่ามันเป็นแนวปฏิบัติที่ดีที่สุดแล้วถ้าคุณกำจัดStreamReaderและเพียงแค่ใช้กระแสโดยตรงในภายหลังคุณจะมีการกำจัดที่เหมาะสม อรรถศาสตร์


2
โอ้ดีมันเกิดขึ้นเมื่อมีการโทรออกเท่านั้นไม่ใช่เมื่อจบขั้นสุดท้าย
Nefzen

1
@ Nezen: นั่นเป็นเพราะไม่มีการรับประกันสิ่งที่สั่งซื้อวัตถุของคุณจะได้รับการสรุป หาก StreamReader และ Stream พื้นฐานนั้นมีสิทธิ์ได้รับการสรุป GC อาจทำการสรุปสตรีมก่อน - จากนั้น Streamreader จะไม่มีการอ้างอิงถึงสตรีม ด้วยเหตุผลนี้คุณสามารถปล่อยทรัพยากรที่ไม่ได้รับการจัดการภายในของขั้นตอนสุดท้ายเท่านั้น (ตัวอย่างเช่น FileStream ปิดตัวจัดการไฟล์ windows ในขั้นตอนสุดท้าย) โอ้และแน่นอนถ้าคุณไม่เคยทิ้งกระแสจะยังคงถูกรวบรวมในที่สุด (และปิดไฟล์) เป็นเพียงการปฏิบัติที่เลวร้ายมากที่จะไม่กำจัดกระแสข้อมูล
JMarsch

13
การซ้อนนี้เป็นสาเหตุให้ตัววิเคราะห์รหัส VS บ่น: 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.ควรเพิกเฉยไหม? ฉันยังไม่ได้รับข้อยกเว้นใด ๆ ...
HB

15
@HB: ปลอดภัยที่จะไม่สนใจในกรณีนี้ StreamReaderหรือคุณก็สามารถสร้างกระแสในการโทรคอนสตรัคไป คำเตือนนั้นดูเป็นการหลอกลวงสำหรับฉันโดยระบุว่าเอกสารสำหรับIDisposable.Disposeสถานะอย่างชัดเจน: "หากวิธีการกำจัดของวัตถุถูกเรียกมากกว่าหนึ่งครั้งวัตถุต้องละเว้นการเรียกทั้งหมดหลังจากการโทรครั้งแรกวัตถุต้องไม่ส่งข้อยกเว้นหากวิธีการกำจัดของมันคือ เรียกหลายครั้ง "
Jon Skeet

5
@ JonSkeet: จริง ๆ แล้วมีหน้าสำหรับสิ่งนี้คุณถูกต้องนี่เป็นของปลอม :)
HB

45

นี่คือสิ่งเก่า แต่ฉันต้องการทำสิ่งที่คล้ายกันในวันนี้และพบว่าสิ่งต่าง ๆ มีการเปลี่ยนแปลง ตั้งแต่. net 4.5 มีleaveOpenอาร์กิวเมนต์:

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

ปัญหาเดียวคือมันไม่ชัดเจนว่าจะตั้งค่าสำหรับพารามิเตอร์อื่น ๆ นี่คือความช่วยเหลือบางส่วน:

จากหน้า msdnสำหรับ StreamReader Constructor (สตรีม):

ตัวสร้างนี้เริ่มต้นการเข้ารหัสเป็น UTF8Encoding คุณสมบัติ BaseStream โดยใช้พารามิเตอร์สตรีมและขนาดบัฟเฟอร์ภายในถึง 1024 ไบต์

แค่ทิ้งไว้detectEncodingFromByteOrderMarksซึ่งการตัดสินโดยซอร์สโค้ดคือtrue

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

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

มันจะดีถ้าค่าเริ่มต้นเหล่านั้นถูกเปิดเผยหรือหากมีข้อโต้แย้งเป็นตัวเลือกเพื่อให้เราสามารถระบุสิ่งที่เราต้องการ


ข้อมูลดีมาก! ไม่เคยได้ยินพารามิเตอร์ใหม่นี้และมันสมเหตุสมผลจริง ๆ
julealgon

3
สำหรับคนขี้เกียจเหมือนผมคำตอบสั้น ๆ ไปยังสตรีมลาเปิดจะเป็นเช่น:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf

29

ใช่แล้ว. คุณสามารถตรวจสอบสิ่งนี้ได้โดยดูที่การใช้งานด้วยตัวสะท้อนแสง

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

หกปีที่ผ่านมา แต่อาจจะช่วยได้บ้าง

StreamReader จะปิดการเชื่อมต่อเมื่อถูกกำจัด อย่างไรก็ตาม "การใช้ (กระแสข้อมูล = ... ) {... }" กับ StreamReader / StreamWriter สามารถส่งผลให้กระแสถูกกำจัดสองครั้ง: (1) เมื่อวัตถุ StreamReader ถูกกำจัด (2) และเมื่อกระแสการใช้บล็อก ปิด สิ่งนี้ส่งผลให้คำเตือน CA2202 เมื่อรันการวิเคราะห์รหัสของ VS

อีกวิธีการแก้ปัญหาที่นำมาโดยตรงจากหน้าCA2202คือการใช้ลอง / ในที่สุดบล็อก ตั้งค่าอย่างถูกต้องซึ่งจะปิดการเชื่อมต่อเพียงครั้งเดียว

ใกล้ด้านล่างของCA2202 Microsoft แนะนำให้ใช้สิ่งต่อไปนี้:

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

แทน...

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

2
คำเตือนจะกล่าวถึงในความคิดเห็นของคำตอบที่ยอมรับเช่นกัน Jon Skeet ให้คำแนะนำที่นั่น
Marcin

นอกจากนี้โปรดทราบว่าคอมไพเลอร์ใช้คำสั่งนั้นถูกแปลงเป็นบล็อก try-สุดท้าย
Jason Kelley

2

ใช่. Calling Dispose () on และ IDisposable (ซึ่ง "ใช้" ทำ) ควรทำให้วัตถุล้างทรัพยากรทั้งหมด ซึ่งรวมถึงการล้างข้อมูลสตรีมและปิดตัวให้คำอธิบายไฟล์

หากในกรณีของคุณคุณต้องการส่งผ่านไปยังวิธีอื่นคุณต้องตรวจสอบให้แน่ใจว่าวิธีการเหล่านั้นไม่ได้ทำการอ่าน / เขียนในบล็อกที่ใช้



-2

สตรีมที่จำหน่ายทั้งโดยใช้คำหลัก "ใช้" หรือโทรทิ้งอย่างชัดเจน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.