แนวทางปฏิบัติที่ดีที่สุดในการใช้ Dispose และ Finalize ใน. Net

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

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

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

Finalizer ถูกเรียกโดยปริยายเมื่อหน่วยความจำที่ครอบครองโดยวัตถุถูกเรียกคืน อย่างไรก็ตาม GC ไม่รับประกันว่าโปรแกรมปิดท้ายจะเรียกใช้ - อาจเรียกหรือไม่เรียกเลยก็ได้ โดยพื้นฐานแล้วโปรแกรมปิดท้ายจะทำงานในโหมดที่ไม่ได้กำหนด - รันไทม์ไม่รับประกันว่าจะมีการเรียกโปรแกรมสุดท้าย อย่างไรก็ตามคุณสามารถบังคับให้เรียก Finalizer ได้แม้ว่าจะไม่ใช่แนวทางปฏิบัติที่ดีเลยเนื่องจากมีบทลงโทษด้านประสิทธิภาพที่เกี่ยวข้อง Finalizers ควรได้รับการปกป้องและควรใช้เพื่อล้างทรัพยากรที่มีการจัดการเท่านั้น คุณไม่ควรจัดสรรหน่วยความจำภายในโปรแกรมสุดท้ายเขียนโค้ดเพื่อใช้ความปลอดภัยของเธรดหรือเรียกใช้วิธีการเสมือนจากภายในโปรแกรมสุดท้าย

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

คุณควรใช้ IDisposable ในทุกประเภทที่มี Finalizer เป็นแนวทางปฏิบัติที่แนะนำให้ใช้ทั้ง Dispose และ Finalize เมื่อคุณมีทรัพยากรที่ไม่มีการจัดการในชั้นเรียนของคุณ

ข้อมูลโค้ดต่อไปนี้แสดงให้เห็นว่าคุณสามารถใช้รูปแบบ Dispose Finalize ใน C # ได้อย่างไร

โมฆะเสมือนที่ได้รับการป้องกันกำจัด (การกำจัดบูล)

        {

            ถ้า (กำจัด)

            {

                // เขียนโค้ดเพื่อล้างอ็อบเจ็กต์ที่มีการจัดการ

            }

            // เขียนโค้ดเพื่อล้างอ็อบเจ็กต์และทรัพยากรที่ไม่มีการจัดการ

        }

เมธอด Dispose ที่กำหนดพารามิเตอร์นี้สามารถเรียกได้โดยอัตโนมัติจาก destructor ดังที่แสดงในข้อมูลโค้ดด้านล่าง

  ~ ทรัพยากร ()

        {

            ถ้า (! ขายทิ้ง)

            {

                จำหน่าย = จริง;

                กำจัด (เท็จ);

            }

        }