เกิดการรั่วไหลของหน่วยความจำหาก MemoryStream ใน. NET ไม่ได้ปิด?


114

ฉันมีรหัสต่อไปนี้:

MemoryStream foo(){
    MemoryStream ms = new MemoryStream();
    // write stuff to ms
    return ms;
}

void bar(){
    MemoryStream ms2 = foo();
    // do stuff with ms2
    return;
}

มีโอกาสไหมที่ MemoryStream ที่ฉันจัดสรรไว้จะล้มเหลวในการกำจัดทิ้งในภายหลัง?

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


41
ถามผู้ตรวจสอบของคุณให้แน่ชัดว่าเหตุใดเขาจึงคิดว่าคุณควรปิด ถ้าเขาพูดถึงการปฏิบัติที่ดีทั่วไปเขาอาจจะฉลาด ถ้าเขาพูดถึงการปลดปล่อยความทรงจำก่อนหน้านี้เขาคิดผิด
Jon Skeet

คำตอบ:


60

หากสิ่งที่ใช้แล้วทิ้งคุณควรกำจัดทิ้งเสมอ คุณควรใช้usingคำสั่งในbar()วิธีการของคุณเพื่อให้แน่ใจว่าms2ได้รับการกำจัด

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


16
การใช้การโทรแบบบล็อกจะทิ้งให้คุณ
Nick

20
@Grauenwolf: การยืนยันของคุณทำลายการห่อหุ้ม ในฐานะผู้บริโภคคุณไม่ควรสนใจว่าสิ่งนั้นจะเป็นสิ่งที่ไม่ควรทำหรือไม่: หากเป็นสิ่งที่ระบุได้มันเป็นหน้าที่ของคุณที่จะต้องทิ้ง () มัน
Marc Gravell

4
นี้ไม่เป็นความจริงสำหรับการเรียน StreamWriter: มันจะทิ้งกระแสที่เกี่ยวโยงกันเท่านั้นถ้าคุณทิ้ง StreamWriter - มันจะไม่เคยทิ้งกระแสหากได้รับการเก็บขยะและ finalizer ของมันได้รับเรียกว่า - นี่คือโดยการออกแบบ
springy76

4
ฉันรู้ว่าคำถามนี้มาจากปี 2008 แต่วันนี้เรามีไลบรารีงาน. NET 4.0 Dispose () ไม่จำเป็นในกรณีส่วนใหญ่ เมื่อใช้ Tasks แม้ว่าฉันจะยอมรับว่า IDisposable ควรหมายถึง "คุณควรกำจัดสิ่งนี้เมื่อคุณทำเสร็จแล้ว" แต่ก็ไม่ได้หมายความอย่างนั้นอีกต่อไป
ฟิลิป

7
อีกตัวอย่างหนึ่งที่คุณไม่ควรกำจัดออบเจ็กต์ IDisposable คือ HttpClient aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong เป็นอีกตัวอย่างหนึ่งจาก BCL ที่มีวัตถุที่สามารถใช้งานได้และคุณไม่จำเป็นต้องกำจัด (หรือไม่ควร) มัน. นี่เป็นเพียงการจำไว้ว่าโดยปกติจะมีข้อยกเว้นบางประการจากกฎทั่วไปแม้ใน BCL;)
Mariusz Pawelski

169

คุณจะไม่รั่วไหลอะไรเลย - อย่างน้อยก็ในการใช้งานปัจจุบัน

การเรียก Dispose จะไม่ล้างหน่วยความจำที่ใช้โดย MemoryStream ให้เร็วขึ้น มันจะหยุดกระแสของคุณจากการทำงานได้สำหรับการอ่าน / เขียนเรียกหลังจากที่โทรซึ่งอาจหรือไม่อาจจะเป็นประโยชน์กับคุณ

หากคุณแน่ใจจริงๆว่าคุณไม่ต้องการย้ายจาก MemoryStream ไปยังสตรีมประเภทอื่นการไม่เรียก Dispose จะไม่ทำอันตรายใด ๆ แต่ก็ปฏิบัติที่ดีโดยทั่วไปส่วนหนึ่งเป็นเพราะถ้าคุณเคยทำการเปลี่ยนแปลงการใช้สตรีมที่แตกต่างกันที่คุณไม่ต้องการที่จะได้รับกัดโดยข้อผิดพลาดที่ยากต่อการค้นหาเนื่องจากคุณเลือกวิธีที่ง่ายออกในช่วงต้น (ในทางกลับกันมีอาร์กิวเมนต์ YAGNI ... )

อีกเหตุผลหนึ่งที่ต้องทำก็คือการใช้งานใหม่อาจแนะนำทรัพยากรที่จะปล่อยให้ทิ้ง


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

ในกรณีนี้ฉันยังคงพยายามกำจัดมันตามหลักการทั่วไป - สร้างนิสัยที่ดี ฯลฯ - แต่ฉันจะไม่กังวลมากเกินไปถ้ามันกลายเป็นเรื่องยุ่งยาก
Jon Skeet

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

@Triynko ไม่จริง: ดู: stackoverflow.com/questions/574019/…สำหรับรายละเอียด
George Stocker

10
อาร์กิวเมนต์ YAGNI สามารถทำได้ทั้งสองวิธี - เนื่องจากการตัดสินใจที่จะไม่ทิ้งบางสิ่งบางอย่างที่ดำเนินการIDisposableเป็นกรณีพิเศษที่ขัดต่อแนวทางปฏิบัติที่ดีที่สุดตามปกติคุณสามารถโต้แย้งได้ว่าเป็นกรณีที่คุณไม่ควรทำจนกว่าคุณจะจำเป็นจริงๆภายใต้ YAGNI หลักการ.
Jon Hanna

26

ใช่มีการรั่วไหลขึ้นอยู่กับว่าคุณกำหนด LEAK ไว้อย่างไรและคุณหมายถึง LATER เท่าไร ...

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

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

ประโยชน์ของคำสั่งการใช้ (เรียกง่ายๆว่า dispose) คือคุณสามารถประกาศการอ้างอิงของคุณในคำสั่งใช้ เมื่อคำสั่งการใช้งานเสร็จสิ้นไม่เพียง แต่ถูกเรียกว่าทิ้งเท่านั้น แต่การอ้างอิงของคุณยังอยู่นอกขอบเขตทำให้การอ้างอิงเป็นโมฆะอย่างมีประสิทธิภาพและทำให้วัตถุของคุณมีสิทธิ์ได้รับการรวบรวมขยะทันทีโดยไม่ต้องจำไว้ว่าต้องเขียนโค้ด "reference = null"

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


6
ฉันชอบคำตอบนี้ บางครั้งผู้คนลืมหน้าที่สองประการในการใช้: การเพิ่มทรัพยากรอย่างกระตือรือร้นและการอ้างอิงอย่างกระตือรือร้น
Kit

1
แม้ว่าฉันจะได้ยินมาว่าไม่เหมือน Java แต่คอมไพลเลอร์ C # ตรวจพบ "การใช้งานครั้งสุดท้าย" ดังนั้นหากตัวแปรถูกกำหนดให้ออกนอกขอบเขตหลังจากการอ้างอิงครั้งสุดท้ายตัวแปรนั้นอาจมีสิทธิ์ได้รับการรวบรวมขยะทันทีหลังจากใช้ครั้งสุดท้าย . ก่อนที่มันจะออกไปนอกขอบเขตจริงๆ. ดูstackoverflow.com/questions/680550/explicit-nulling
Triynko

2
คนเก็บขยะและเครื่องกระวนกระวายใจไม่ทำงานในลักษณะนั้น ขอบเขตเป็นการสร้างภาษาไม่ใช่สิ่งที่รันไทม์จะปฏิบัติตาม ในความเป็นจริงคุณอาจจะยืดเวลาที่การอ้างอิงอยู่ในหน่วยความจำให้ยาวขึ้นโดยเพิ่มการเรียกไปที่. Dispose () เมื่อบล็อกสิ้นสุดลง ดูericlippert.com/2015/05/18/…
Pablo Montilla

8

ไม่จำเป็นต้องโทร.Dispose()(หรือห่อด้วยUsing)

เหตุผลที่คุณโทรหา.Dispose()คือการปล่อยทรัพยากรโดยเร็วที่สุดเท่าที่เป็นไปได้

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


24
การเรียก Dispose บน MemoryStream จะไม่ปล่อยหน่วยความจำใด ๆ ในความเป็นจริงคุณยังสามารถรับข้อมูลใน MemoryStream ได้หลังจากที่คุณเรียก Dispose - ลองดู :)
Jon Skeet

12
-1 แม้ว่าจะเป็นเรื่องจริงสำหรับ MemoryStream ก็ตามคำแนะนำทั่วไปนี่เป็นเพียงความผิดธรรมดา Dispose คือการปล่อยทรัพยากรที่ไม่มีการจัดการเช่นการจัดการไฟล์หรือการเชื่อมต่อฐานข้อมูล หน่วยความจำไม่อยู่ในประเภทนั้น คุณควรรอการรวบรวมขยะตามกำหนดเวลาเพื่อให้หน่วยความจำว่าง
โจ

1
อะไรคือข้อดีของการนำรูปแบบการเข้ารหัสมาใช้ในการจัดสรรและกำจัดอFileStreamอบเจ็กต์และอีกแบบหนึ่งสำหรับMemoryStreamอ็อบเจ็กต์
Robert Rossney

3
FileStream เกี่ยวข้องกับทรัพยากรที่ไม่มีการจัดการซึ่งสามารถปลดปล่อยได้ทันทีเมื่อเรียก Dispose ในทางกลับกัน MemoryStream เก็บอาร์เรย์ไบต์ที่มีการจัดการไว้ในตัวแปร _buffer ซึ่งไม่ได้รับการปลดปล่อยเมื่อถึงเวลากำจัด ในความเป็นจริง _buffer ไม่ได้ถูกลบล้างในเมธอด Dispose ของ MemoryStream ซึ่งเป็น IMO BUG ที่น่าอับอายเนื่องจากการลบข้อมูลอ้างอิงอาจทำให้หน่วยความจำมีสิทธิ์สำหรับ GC ในเวลาที่จำหน่าย แต่การอ้างอิง MemoryStream ที่เอ้อระเหย (แต่ถูกจำหน่ายทิ้ง) ยังคงยึดติดกับหน่วยความจำ ดังนั้นเมื่อคุณกำจัดทิ้งคุณควรลบทิ้งหากยังอยู่ในขอบเขต
Triynko

@Triynko - "ดังนั้นเมื่อคุณกำจัดทิ้งไปแล้วคุณควรจะลบทิ้งหากยังอยู่ในขอบเขต" - ฉันไม่เห็นด้วย หากมีการใช้อีกครั้งหลังจากเรียก Dispose สิ่งนี้จะทำให้เกิด NullReferenceException หากไม่ได้ใช้อีกหลังจากทิ้งไปก็ไม่จำเป็นต้องโมฆะ GC ฉลาดพอ
โจ

8

นี่เป็นคำตอบแล้ว แต่ฉันจะเพิ่มว่าหลักการซ่อนข้อมูลแบบเก่าที่ดีหมายความว่าในอนาคตคุณอาจต้องการ refactor:

MemoryStream foo()
{    
    MemoryStream ms = new MemoryStream();    
    // write stuff to ms    
    return ms;
}

ถึง:

Stream foo()
{    
   ...
}

สิ่งนี้เน้นว่าผู้โทรไม่ควรสนใจว่าสตรีมจะถูกส่งกลับประเภทใดและทำให้สามารถเปลี่ยนการใช้งานภายในได้ (เช่นเมื่อจำลองการทดสอบหน่วย)

จากนั้นคุณจะต้องประสบปัญหาหากคุณไม่ได้ใช้ Dispose ในการใช้งานบาร์ของคุณ:

void bar()
{    
    using (Stream s = foo())
    {
        // do stuff with s
        return;
    }
}

5

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

ทุกที่ที่คุณโทรหา Foo คุณสามารถทำได้โดยใช้ (MemoryStream ms = foo ()) และฉันคิดว่าคุณน่าจะยังโอเค


1
ปัญหาหนึ่งที่ฉันเคยพบกับนิสัยนี้คือคุณต้องแน่ใจว่าสตรีมไม่ได้ถูกใช้ที่อื่น ตัวอย่างเช่นฉันสร้าง JpegBitmapDecoder ที่ชี้ไปที่ MemoryStream และส่งคืน Frames [0] (คิดว่ามันจะคัดลอกข้อมูลไปยังที่จัดเก็บภายในของตัวเอง) แต่พบว่าบิตแมปจะแสดงเพียง 20% ของเวลา - ปรากฎว่าเป็นเพราะ ฉันกำลังกำจัดสตรีมแห่งความทรงจำ
devios1

หากสตรีมหน่วยความจำของคุณต้องคงอยู่ (เช่นบล็อกการใช้งานไม่สมเหตุสมผล) คุณควรเรียก Dispose และตั้งค่าตัวแปรเป็น null ทันที หากคุณทิ้งมันก็จะไม่ถูกนำมาใช้อีกต่อไปดังนั้นคุณควรตั้งค่าเป็นโมฆะทันที สิ่งที่ Chaiguy อธิบายดูเหมือนเป็นปัญหาการจัดการทรัพยากรเพราะคุณไม่ควรให้การอ้างอิงถึงบางสิ่งเว้นแต่สิ่งที่คุณมอบให้จะรับผิดชอบในการกำจัดและสิ่งที่มอบให้การอ้างอิงนั้นรู้ว่ามันไม่รับผิดชอบอีกต่อไป ทำเช่นนั้น
Triynko

2

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

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


1
> คุณยังไม่ได้ใช้หน่วยความจำรั่ว แต่คุณกำลังขยายระยะเวลาที่คุณอ้างว่าใช้งานไปโดยไม่จำเป็น คุณแน่ใจไหม? การกำจัดไม่ปล่อยหน่วยความจำและการเรียกใช้งานล่าช้าในฟังก์ชันอาจขยายเวลาที่ไม่สามารถรวบรวมได้
Jonathan Allen

2
ใช่โจนาธานมีประเด็น การวางสายเพื่อ Dispose ล่าช้าในฟังก์ชันอาจทำให้คอมไพลเลอร์คิดว่าคุณต้องเข้าถึงอินสแตนซ์สตรีม (เพื่อปิด) ในฟังก์ชันล่าช้ามาก สิ่งนี้อาจเลวร้ายยิ่งกว่าการไม่เรียกใช้ dispose เลย (ดังนั้นจึงหลีกเลี่ยงการอ้างอิงฟังก์ชัน late-in-function ไปยังตัวแปร stream) เนื่องจากคอมไพลเลอร์สามารถคำนวณจุดปล่อยที่เหมาะสมที่สุด (aka "point of last possible use") ก่อนหน้าในฟังก์ชัน .
Triynko

2

ฉันอยากจะแนะนำให้รวม MemoryStream ไว้bar()ในusingคำสั่งเพื่อความสอดคล้องเป็นหลัก:

  • ตอนนี้ MemoryStream ไม่ได้เปิดหน่วยความจำ.Dispose()ให้ว่าง แต่เป็นไปได้ว่าในอนาคตอาจมีหรือคุณ (หรือคนอื่นใน บริษัท ของคุณ) อาจแทนที่ด้วย MemoryStream ที่คุณกำหนดเองซึ่งทำเป็นต้น
  • ช่วยสร้างรูปแบบในโปรเจ็กต์ของคุณเพื่อให้แน่ใจว่าสตรีมทั้งหมดจะได้รับการกำจัด - เส้นจะถูกวาดอย่างแน่นหนามากขึ้นโดยการพูดว่า "ต้องกำจัดสตรีมทั้งหมด" แทนที่จะเป็น "สตรีมบางรายการต้องถูกกำจัด แต่บางสตรีมไม่จำเป็นต้อง" ...
  • หากคุณเคยเปลี่ยนรหัสเพื่ออนุญาตให้ส่งคืนสตรีมประเภทอื่นคุณจะต้องเปลี่ยนรหัสเพื่อกำจัดต่อไป

อีกสิ่งหนึ่งที่ฉันมักจะทำในกรณีเช่นfoo()เมื่อสร้างและส่งคืน IDisposable คือเพื่อให้แน่ใจว่าความล้มเหลวระหว่างการสร้างวัตถุและreturnข้อยกเว้นถูกจับโดยข้อยกเว้นทิ้งวัตถุและเรียกคืนข้อยกเว้นใหม่:

MemoryStream x = new MemoryStream();
try
{
    // ... other code goes here ...
    return x;
}
catch
{
    // "other code" failed, dispose the stream before throwing out the Exception
    x.Dispose();
    throw;
}

1

หากวัตถุใช้ IDisposable คุณต้องเรียกใช้เมธอด. Dispose เมื่อคุณทำเสร็จแล้ว

ในบางออบเจ็กต์ Dispose มีความหมายเหมือนกับปิดและในทางกลับกันในกรณีนั้นก็ดีเช่นกัน

ตอนนี้สำหรับคำถามเฉพาะของคุณไม่คุณจะไม่ทำให้หน่วยความจำรั่วไหล


3
"ต้อง" เป็นคำที่แรงมาก เมื่อใดก็ตามที่มีกฎคุณควรรู้ถึงผลที่ตามมาของการทำลายกฎเหล่านั้น สำหรับ MemoryStream ผลที่ตามมามีน้อยมาก
Jon Skeet

-1

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


MemoryStream อยู่ในหน่วยความจำทั้งหมด - ไม่มีที่จับไฟล์ที่นี่
Jon Skeet

-2

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


1
ไม่ตรงตามความเป็นจริง Dispose ถูกเรียกเมื่อออกจากคำสั่ง use ไม่เรียกว่า Dispose เมื่อวัตถุอยู่นอกขอบเขต
Alexander Abramov

-3

MemorySteram คืออะไรนอกจากอาร์เรย์ของไบต์ซึ่งเป็นวัตถุที่มีการจัดการ การลืมทิ้งหรือปิดสิ่งนี้ไม่มีผลข้างเคียงใด ๆ นอกเหนือจากการสรุปผล
เพียงตรวจสอบตัวสร้างหรือวิธีการล้างของ MemoryStream ในตัวสะท้อนแสงและจะชัดเจนว่าทำไมคุณไม่จำเป็นต้องกังวลเกี่ยวกับการปิดหรือกำจัดมันนอกเหนือจากการปฏิบัติตามแนวทางปฏิบัติที่ดีเท่านั้น


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