การใช้งานที่ถูกต้องของ IDisposable อินเท
อร์เฟซที่ใช้ในการนำออกใช้ทรัพยากรออบเจ็กต์ใน .NET คือ IDisposable แต่การใช้งานอินเทอร์เฟซนี้ค่อนข้างเฉพาะเจาะจง นอกจากนี้ ยังมีฟังก์ชันสองรายการ: สิ้นสุดและปิด
MSDN แนะนำให้ใช้อินเทอร์เฟซ IDisposable ตามรูปแบบต่อไปนี้:
1 คลาสสาธารณะ Foo: IDisposable
2 {
3 โมฆะสาธารณะกำจัด()
4 {
5 กำจัด (จริง);
6 GC.SuppressFinalize (สิ่งนี้);
7}
8
9 โมฆะเสมือนที่ได้รับการป้องกัน การกำจัด (การกำจัดบูล)
10 {
11 ถ้า (!m_disposed)
12 {
13 ถ้า (ทิ้ง)
14 {
15 // ปล่อยทรัพยากรที่ได้รับการจัดการ
16}
17
18 // ปล่อยทรัพยากรที่ไม่มีการจัดการ
19
20 m_disposed = จริง;
ยี่สิบเอ็ด }
ยี่สิบสอง }
ยี่สิบสาม
24 ~ฟู()
25 {
26 กำจัด (เท็จ);
27}
28
29 บูลส่วนตัว m_disposed;
30}
31
32
จริงๆ แล้วมีสองฟังก์ชันในการปล่อยทรัพยากรในออบเจ็กต์ .NET: Dispose และ Finalize วัตถุประสงค์ของ Finalize คือการปล่อยทรัพยากรที่ไม่มีการจัดการ ในขณะที่ Dispose ใช้เพื่อปล่อยทรัพยากรทั้งหมด รวมถึงที่มีการจัดการและไม่ได้รับการจัดการ
ในโหมดนี้ ฟังก์ชัน void Dispose(bool disposing) จะใช้พารามิเตอร์ disposing เพื่อแยกแยะว่า Dispose() กำลังถูกเรียกอยู่หรือไม่ หากถูกเรียกโดย Dispose() ทั้งทรัพยากรที่ได้รับการจัดการและไม่ได้รับการจัดการจะต้องได้รับการเผยแพร่พร้อมกัน หากถูกเรียกโดย ~Foo() (นั่นคือ Finalize() ของ C#) คุณจะต้องปล่อยทรัพยากรที่ไม่มีการจัดการเท่านั้น
นี่เป็นเพราะว่าฟังก์ชัน Dispose() ถูกเรียกอย่างชัดเจนด้วยโค้ดอื่น และจำเป็นต้องมีการเผยแพร่ทรัพยากร ในขณะที่ Finalize ถูกเรียกโดย GC วัตถุที่ได้รับการจัดการอื่นๆ ที่ Foo อ้างอิงอาจไม่จำเป็นต้องถูกทำลายเมื่อมีการเรียก GC และแม้ว่าจะถูกทำลายไปแล้วก็ตาม GC ก็จะถูกเรียกวัตถุเหล่านั้น ดังนั้น เฉพาะทรัพยากรที่ไม่มีการจัดการเท่านั้นที่ต้องได้รับการเผยแพร่ในการสรุปผล ในทางกลับกัน เนื่องจากทรัพยากรที่มีการจัดการและไม่มีการจัดการได้รับการเผยแพร่ใน Dispose() จึงไม่จำเป็นต้องเรียก Finalize อีกครั้งเมื่อ GC รีไซเคิลอ็อบเจ็กต์ ดังนั้นให้เรียก GC.SuppressFinalize(this) ใน Dispose() เพื่อหลีกเลี่ยงการเรียกซ้ำ จบ
อย่างไรก็ตาม ไม่มีปัญหาแม้ว่าการสรุปและการกำจัดจะถูกเรียกซ้ำๆ กัน เนื่องจากการมีอยู่ของตัวแปร m_disposed ทรัพยากรจะถูกรีลีสเพียงครั้งเดียว และการเรียกที่ซ้ำซ้อนจะถูกละเว้น
ดังนั้นรูปแบบข้างต้นจึงรับประกันได้ว่า:
1. ปิดท้ายเฉพาะทรัพยากรที่ไม่มีการจัดการ
2. กำจัดทรัพยากรที่มีการจัดการและไม่ได้รับการจัดการ
3. ไม่มีปัญหาในการเรียกสรุปและกำจัดซ้ำๆ
4. สรุปและกำจัดใช้กลยุทธ์การปล่อยทรัพยากรเดียวกัน ดังนั้นจึงไม่มีความขัดแย้งระหว่างกัน .
ในภาษา C# รูปแบบนี้จะต้องถูกนำมาใช้อย่างชัดเจน โดยที่ฟังก์ชัน ~Foo() ของ C# แสดงถึง Finalize() ใน C++/CLI โหมดนี้จะถูกนำไปใช้โดยอัตโนมัติ แต่ตัวทำลายคลาส C++ นั้นแตกต่างออกไป
ตามซีแมนทิกส์ของ C++ ตัวทำลายจะถูกเรียกเมื่ออยู่นอกขอบเขตหรือถูกลบ ใน C++ ที่มีการจัดการ (นั่นคือ C++ ที่มีการจัดการใน .NET 1.1) destructor จะเทียบเท่ากับเมธอด Finalize() ใน CLR ซึ่งถูกเรียกโดย GC ในระหว่างการรวบรวมขยะ ดังนั้น ระยะเวลาของการโทรจึงไม่ชัดเจน ใน C++/CLI ใน .NET 2.0 ความหมายของ destructors ได้รับการแก้ไขให้เทียบเท่ากับเมธอด Dispose() ซึ่งมีความหมายสองสิ่ง:
1. คลาส CLR ทั้งหมดใน C++/CLI ใช้อินเทอร์เฟซ IDisposable ดังนั้นคุณสามารถใช้คีย์เวิร์ดที่ใช้เพื่อเข้าถึงอินสแตนซ์ของคลาสนี้ใน C#
2. destructor ไม่เทียบเท่ากับ Finalize() อีกต่อไป
สำหรับประเด็นแรก นี่เป็นสิ่งที่ดี ฉันคิดว่า Dispose() เชิงความหมายนั้นใกล้กับตัวทำลาย C++ มากกว่า เกี่ยวกับประเด็นที่สอง Microsoft ได้สร้างส่วนขยายโดยแนะนำฟังก์ชัน "!" ดังที่แสดงด้านล่าง:
1 คลาสการอ้างอิงสาธารณะ Foo
2 {
3 สาธารณะ:
4ฟู();
5 ~Foo(); // ตัวทำลาย
6 !Foo(); // เข้ารอบสุดท้าย
7};
8
ฟังก์ชัน "!" (ฉันไม่รู้ว่าจะเรียกมันว่าอะไร) แทนที่ Finalize() ดั้งเดิมใน Managed C++ และถูกเรียกโดย GC MSDN แนะนำว่าเพื่อลดความซ้ำซ้อนของโค้ด คุณสามารถเขียนโค้ดดังนี้:
1 ~Foo()
2 {
3 //ปล่อยทรัพยากรที่มีการจัดการ
4 นี้->!Foo();
5}
6
7 !ฟู()
8 {
9 // ปล่อยทรัพยากรที่ไม่มีการจัดการ
10}
11
สำหรับคลาสข้างต้น รหัส C# ที่เกี่ยวข้องซึ่งสร้างโดย C++/CLI จะเป็นดังนี้:
1 ชั้นเรียนสาธารณะฟู
2 {
3 โมฆะส่วนตัว !Foo()
4 {
5 // ปล่อยทรัพยากรที่ไม่มีการจัดการ
6}
7
8 โมฆะส่วนตัว ~Foo()
9 {
10 // ปล่อยทรัพยากรที่ได้รับการจัดการ
11 !ฟู();
12}
13
14 สาธารณะฟู()
15 {
16}
17
18 โมฆะสาธารณะกำจัด()
19 {
20 กำจัด (จริง);
21 GC.SuppressFinalize(นี่);
ยี่สิบสอง }
ยี่สิบสาม
การกำจัดโมฆะเสมือนที่ได้รับการป้องกัน 24 รายการ (การกำจัดบูล)
25 {
26 ถ้า (ทิ้ง)
27 {
28 ~ฟู();
29 }
30 อื่น ๆ
31 {
32 ลอง
33 {
34 !ฟู();
35}
36 ในที่สุด
37 {
38 ฐานสรุป ();
39 }
40}
41 }
42
43 โมฆะที่ได้รับการป้องกันสรุป ()
44 {
45 กำจัด (เท็จ);
46 }
47 }
48
เนื่องจาก ~Foo() และ !Foo() จะไม่ถูกเรียกซ้ำๆ (อย่างน้อย MS ก็คิดอย่างนั้น) จึงไม่มีตัวแปรในโค้ดนี้ที่เหมือนกับ m_disposed ก่อนหน้า แต่โครงสร้างพื้นฐานจะเหมือนกัน
ยิ่งไปกว่านั้น คุณจะเห็นว่าจริงๆ แล้วไม่ใช่ ~Foo() และ !Foo() ที่เป็น Dispose และ Finalize แต่คอมไพเลอร์ C++/CLI จะสร้างฟังก์ชัน Dispose และ Finalize สองฟังก์ชัน และเรียกใช้ฟังก์ชันเหล่านั้นในเวลาที่เหมาะสม C++/CLI ทำงานได้หลายอย่างจริงๆ แต่ปัญหาเดียวก็คือมันต้องอาศัยการที่ผู้ใช้เรียก !Foo() ใน ~Foo()
เกี่ยวกับการเผยแพร่ทรัพยากร สิ่งสุดท้ายที่ต้องพูดถึงคือฟังก์ชันปิด มีความหมายคล้ายกับ Dispose มาก ตามข้อมูลของ MSDN ฟังก์ชันนี้มีไว้เพื่อให้ผู้ใช้รู้สึกสบายใจมากขึ้น เนื่องจากผู้ใช้คุ้นเคยกับการเรียก Close() สำหรับวัตถุบางอย่าง เช่น ไฟล์ มากกว่า
อย่างไรก็ตาม ทั้งสองฟังก์ชันนี้ทำสิ่งเดียวกัน ดังนั้นโค้ดที่แนะนำโดย MSDN คือ:
1 โมฆะสาธารณะ ปิด()
2 {
3 กำจัด(();
4}
5
6
ในที่นี้ ฟังก์ชัน Dispose ที่ไม่มีพารามิเตอร์จะถูกเรียกโดยตรงเพื่อให้ได้ซีแมนทิกส์เดียวกันกับ Dispose ดูเหมือนว่าจะสมบูรณ์แบบ แต่ในทางกลับกัน หากมีการระบุ Dispose และ Close ในเวลาเดียวกัน ก็จะทำให้ผู้ใช้สับสนได้ หากไม่เห็นรายละเอียดของโค้ด เป็นการยากที่จะทราบความแตกต่างระหว่างฟังก์ชันทั้งสองนี้ ดังนั้น ข้อกำหนดการออกแบบโค้ด .NET ระบุว่าผู้ใช้สามารถใช้ฟังก์ชันใดฟังก์ชันหนึ่งจากสองฟังก์ชันนี้เท่านั้น ดังนั้นรูปแบบที่แนะนำคือ:
1 คลาสสาธารณะ Foo: IDisposable
2 {
3 โมฆะสาธารณะ ปิด()
4 {
5 กำจัด();
6}
7
8 โมฆะ IDisposable.Dispose()
9 {
10 กำจัด (จริง);
11 GC.SuppressFinalize(นี่);
12}
13
14 การกำจัดโมฆะเสมือนที่ได้รับการป้องกัน (การกำจัดบูล)
15 {
16 //เหมือนเดิม.
17}
18}
19
มีการใช้สิ่งที่เรียกว่าการใช้งานอินเทอร์เฟซอย่างชัดเจนที่นี่: void IDisposable.Dispose() การใช้งานที่ชัดเจนนี้สามารถเข้าถึงได้ผ่านอินเทอร์เฟซเท่านั้น แต่ไม่สามารถผ่านคลาสการใช้งานได้ ดังนั้น:
1 ฟู ฟู = ฟูใหม่();
2
3 foo.Dispose(); // เกิดข้อผิดพลาด
4 (foo เป็น IDisposable).Dispose(); // แก้ไข
5
สิ่งนี้จะดูแลทั้งสองอย่าง สำหรับผู้ที่ชอบใช้ Close ก็สามารถใช้ foo.Close() ได้โดยตรง แต่จะไม่เห็น Dispose() สำหรับผู้ที่ชอบ Dispose เขาสามารถเปลี่ยนประเภทเป็น IDisposable เพื่อโทร หรือใช้คำสั่งusing มีความสุขมากกับทั้งคู่!
http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html