มีวิธีใดในการปิด StreamWriter โดยไม่ต้องปิด BaseStream หรือไม่


117

ปัญหาหลักของฉันคือเมื่อusingเรียกDisposeใช้ a StreamWriterมันจะกำจัดBaseStream(ปัญหาเดียวกันกับClose)

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

จุดประสงค์ของสิ่งนี้คือการรับเนื้อหาของสตริง (เดิมอ่านจากฐานข้อมูล) ไปยังสตรีมดังนั้นส่วนประกอบของบุคคลที่สามจึงสามารถอ่านสตรีมได้
หมายเหตุ : ฉันไม่สามารถเปลี่ยนองค์ประกอบของบุคคลที่สามได้

public System.IO.Stream CreateStream(string value)
{
    var baseStream = new System.IO.MemoryStream();
    var baseCopy = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        baseStream.WriteTo(baseCopy); 
    }
    baseCopy.Seek(0, System.IO.SeekOrigin.Begin);
    return baseCopy;
}

ใช้เป็น

public void Noddy()
{
    System.IO.Stream myStream = CreateStream("The contents of this string are unimportant");
    My3rdPartyComponent.ReadFromStream(myStream);
}

ตามหลักการแล้วฉันกำลังมองหาวิธีการจินตภาพที่เรียกว่าBreakAssociationWithBaseStreamเช่น

public System.IO.Stream CreateStream_Alternate(string value)
{
    var baseStream = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        writer.BreakAssociationWithBaseStream();
    }
    return baseStream;
}

นี่เป็นคำถามที่คล้ายกัน: stackoverflow.com/questions/2620851
Jens Granlund

ฉันกำลังทำสิ่งนี้กับสตรีมจาก WebRequest ที่น่าสนใจคุณสามารถปิดได้หากการเข้ารหัสเป็น ASCII แต่ไม่ใช่ UTF8 แปลก.
tofutim

tofutim ฉันเข้ารหัสของฉันเป็น ASCII และมันยังคงปล่อยสตรีมที่อยู่เบื้องหลัง ..
เจอราร์ดออน

คำตอบ:


121

หากคุณกำลังใช้ .NET Framework 4.5 หรือในภายหลังมีStreamWriter เกินพิกัดใช้ซึ่งคุณสามารถขอให้กระแสฐานที่จะเปิดทิ้งไว้เมื่อนักเขียนถูกปิด

ใน. NET Framework เวอร์ชันก่อนหน้า 4.5 StreamWriter ถือว่าเป็นเจ้าของสตรีม ตัวเลือก:

  • อย่าทิ้งStreamWriter; เพียงแค่ล้างมัน
  • สร้างเสื้อคลุมสตรีมที่ละเว้นการเรียกไปยังClose/ Disposeแต่พร็อกซีทุกอย่างพร้อมกัน ฉันมีการนำสิ่งนั้นไปใช้ในMiscUtilหากคุณต้องการคว้ามันจากที่นั่น

15
เห็นได้ชัดว่าการโอเวอร์โหลด 4.5 นั้นไม่ได้คำนึงถึงสัมปทาน - การโอเวอร์โหลดต้องการขนาดบัฟเฟอร์ซึ่งไม่สามารถเป็น 0 หรือว่างได้ ภายในฉันรู้ว่า 128 ตัวอักษรเป็นขนาดขั้นต่ำดังนั้นฉันจึงตั้งค่าเป็น 1 มิฉะนั้น 'คุณสมบัติ' นี้จะทำให้ฉันมีความสุข
Gerard ONeill

มีวิธีตั้งค่าleaveOpenพารามิเตอร์หลังจากที่StreamWriterสร้างขึ้นหรือไม่?
c00000fd

@ c00000fd: ไม่ใช่ว่าฉันจะรู้
Jon Skeet

1
@Yepeekai: "ถ้าฉันส่งสตรีมไปยังเมธอดย่อยและเมธอดย่อยนั้นสร้าง StreamWriter มันจะถูกกำจัดเมื่อสิ้นสุดการดำเนินการของเมธอดย่อยนั้น" ไม่นั่นไม่เป็นความจริง ก็จะถูกกำจัดถ้าสิ่งที่เรียกร้องDisposeเกี่ยวกับมัน การสิ้นสุดวิธีการไม่ทำโดยอัตโนมัติ อาจมีการสรุปในภายหลังหากมีการสรุปผล แต่นั่นไม่ใช่สิ่งเดียวกัน - และยังไม่ชัดเจนว่าคุณคาดว่าจะเกิดอันตรายใด หากคุณคิดว่าไม่ปลอดภัยที่จะส่งคืน a StreamWriterจากวิธีการเนื่องจาก GC อาจถูกกำจัดโดยอัตโนมัตินั่นไม่เป็นความจริง
Jon Skeet

1
@Yepeekai: และ IIRC StreamWriterไม่มีFinalizer - ฉันไม่คาดหวังด้วยเหตุผลนี้
Jon Skeet

44

.NET 4.5 มีวิธีการใหม่ที่น่าสนใจ!

http://msdn.microsoft.com/EN-US/library/gg712853(v=VS.110,d=hv.2).aspx

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

ขอบคุณเพื่อน! ไม่ทราบเรื่องนี้และหากมีสิ่งใดที่จะเป็นเหตุผลที่ดีสำหรับฉันในการเริ่มกำหนดเป้าหมาย. NET 4.5!
Vectovox

22
ความอัปยศไม่มีการโอเวอร์โหลดที่ไม่ต้องตั้งค่า bufferSize ฉันพอใจกับค่าเริ่มต้นที่นั่น ฉันต้องผ่านมันไปด้วยตัวเอง ไม่ใช่จุดจบของโลก
Andy McCl luggage

3
ค่าเริ่มต้นคือbufferSize โดยมีรายละเอียดที่นี่ 1024
Alex Klaus

35

อย่าเรียกDisposeใช้ไฟล์StreamWriter. เหตุผลที่คลาสนี้ใช้แล้วทิ้งไม่ใช่เพราะมีทรัพยากรที่ไม่มีการจัดการ แต่เพื่อให้สามารถกำจัดสตรีมซึ่งตัวมันเองสามารถเก็บทรัพยากรที่ไม่มีการจัดการได้ หากชีวิตของกระแสต้นแบบถูกจัดการที่อื่นก็ไม่จำเป็นต้องทิ้งนักเขียน


2
@ มาร์คจะไม่โทรFlushมาทำงานในกรณีที่บัฟเฟอร์ข้อมูล?
Darin Dimitrov

3
ดี แต่เมื่อเราออกจาก CreateStream แล้ว StreamWrtier จะสามารถรวบรวมได้โดยบังคับให้ผู้อ่านส่วนที่สามแข่งกับ GC ซึ่งไม่ใช่สถานการณ์ที่ฉันต้องการทิ้งไว้หรือฉันพลาดอะไรไป
Binary Worrier

9
@BinaryWorrier: ไม่ไม่มีเงื่อนไขการแข่งขัน: StreamWriter ไม่มีโปรแกรมสุดท้าย (และไม่ควร)
Jon Skeet

10
@Binary Worrier: คุณควรมีเพียง finalizer ถ้าคุณโดยตรงเป็นเจ้าของทรัพยากร ในกรณีนี้ StreamWriter ควรสันนิษฐานว่า Stream จะล้างตัวเองหากจำเป็น
Jon Skeet

2
ดูเหมือนว่าเมธอด 'ปิด' ของ StreamWriter จะปิดและจำหน่ายสตรีมด้วยเช่นกัน ดังนั้นเราจึงต้องล้าง แต่ไม่ปิดหรือกำจัดผู้เขียนกระแสดังนั้นจึงไม่ปิดสตรีมซึ่งจะเทียบเท่ากับการกำจัดสตรีม "ความช่วยเหลือ" จาก API มากเกินไปที่นี่
Gerard ONeill

5

สตรีมหน่วยความจำมีคุณสมบัติ ToArray ที่สามารถใช้ได้แม้ว่าสตรีมจะถูกปิด ในการ Array เขียนเนื้อหาสตรีมไปยังอาร์เรย์ไบต์โดยไม่คำนึงถึงคุณสมบัติตำแหน่ง คุณสามารถสร้างสตรีมใหม่ตามสตรีมที่คุณเขียน

public System.IO.Stream CreateStream(string value)
{
    var baseStream = new System.IO.MemoryStream();
    var baseCopy = new System.IO.MemoryStream();
    using (var writer = new System.IO.StreamWriter(baseStream, System.Text.Encoding.UTF8))
    {
        writer.Write(value);
        writer.Flush();
        baseStream.WriteTo(baseCopy); 
    }
    var returnStream = new System.IO.MemoryStream( baseCopy.ToArray());
    return returnStream;
}

นั่น จำกัด อาร์เรย์ที่ส่งคืนให้มีขนาดเนื้อหาอย่างถูกต้องหรือไม่ เพราะStream.Positionสามารถไม่ได้จะเรียกว่าหลังจากที่มันเป็นที่จำหน่าย
Nyerguds

2

คุณต้องสร้างทายาทของ StreamWriter และแทนที่วิธีการกำจัดโดยการส่งเท็จไปยังพารามิเตอร์การกำจัดเสมอมันจะบังคับให้ผู้เขียนสตรีมไม่ปิด StreamWriter เพียงแค่เรียกการกำจัดด้วยวิธีปิดดังนั้นจึงไม่จำเป็นต้อง แทนที่มัน (แน่นอนคุณสามารถเพิ่มตัวสร้างทั้งหมดได้ถ้าคุณต้องการฉันมีแค่อันเดียว):

public class NoCloseStreamWriter : StreamWriter
{
    public NoCloseStreamWriter(Stream stream, Encoding encoding)
        : base(stream, encoding)
    {
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(false);
    }
}

3
ฉันเชื่อว่าสิ่งนี้ไม่ได้ทำอย่างที่คุณคิด disposingธงเป็นส่วนหนึ่งของรูปแบบ การส่งผ่านไปยังเมธอดของคลาสพื้นฐานเสมอโดยทั่วไปจะส่งสัญญาณว่ากำลังถูกเรียกจากโปรแกรมสุดท้าย (ซึ่งไม่เป็นเช่นนั้นเมื่อคุณเรียกอย่างชัดเจน) ดังนั้นจึงไม่ควรเข้าถึงอ็อบเจ็กต์ที่มีการจัดการใด ๆ นี่คือเหตุผลว่าทำไมจึงไม่ทิ้งสตรีมฐาน อย่างไรก็ตามวิธีที่คุณประสบความสำเร็จคือการแฮ็ก มันจะง่ายกว่ามากถ้าไม่โทรมาตั้งแต่แรก! IDisposablefalseDispose(bool)StreamWriterDispose()Dispose
stakx - ไม่ร่วมให้ข้อมูลใน

นั่นคือไซแมนเทคจริงๆสิ่งที่คุณทำนอกเหนือจากการเขียนสตรีมมิงใหม่ทั้งหมดตั้งแต่เริ่มต้นจะเป็นการแฮ็ก แน่นอนคุณไม่สามารถเรียกฐานได้กำจัด (เท็จ) เลย แต่จะไม่มีความแตกต่างในการทำงานและฉันชอบความชัดเจนของตัวอย่างของฉัน อย่างไรก็ตามโปรดจำไว้ว่าคลาส StreamWriter เวอร์ชันในอนาคตอาจทำมากกว่าเพียงแค่ปิดสตรีมเมื่อมันถูกทิ้งดังนั้นการเรียกใช้ dispose (false) ในอนาคตจะเป็นการพิสูจน์เช่นกัน แต่สำหรับเขาแต่ละคน
Aaron Murgatroyd

2
อีกวิธีในการทำเช่นนี้คือการสร้าง stream wrapper ของคุณเองซึ่งมีสตรีมอื่นโดยที่วิธีการปิดไม่ได้ทำอะไรเลยแทนที่จะปิดสตรีมที่อยู่เบื้องหลังนี่เป็นการแฮ็กน้อยกว่า แต่ทำงานได้ดีกว่า
Aaron Murgatroyd

ช่วงเวลาที่น่าทึ่ง: ฉันแค่กำลังจะแนะนำสิ่งเดียวกัน (คลาสมัณฑนากรซึ่งอาจตั้งชื่อOwnedStreamที่เพิกเฉยDispose(bool)และClose)
stakx - ไม่ร่วมให้ข้อมูลใน

ใช่โค้ดด้านบนคือวิธีที่ฉันทำเพื่อวิธีที่รวดเร็วและสกปรก แต่ถ้าฉันสร้างแอปพลิเคชันเชิงพาณิชย์หรือสิ่งที่สำคัญสำหรับฉันจริง ๆ ฉันจะทำอย่างถูกต้องโดยใช้คลาส Stream wrapper โดยส่วนตัวแล้วฉันคิดว่า Microsoft ทำผิดพลาดที่นี่ผู้เขียนสตรีมควรมีคุณสมบัติบูลีนเพื่อปิดสตรีมพื้นฐานแทน แต่ฉันไม่ได้ทำงานที่ Microsoft ดังนั้นพวกเขาจึงทำในสิ่งที่พวกเขาชอบ: D
Aaron Murgatroyd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.