จบลงด้วยการกำจัด


215

ทำไมบางคนถึงใช้Finalizeวิธีนี้มากกว่าDisposeวิธี?

ในสถานการณ์ใดบ้างที่คุณจะใช้Finalizeวิธีนี้เหนือDisposeวิธีการและในทางกลับกัน


1
มีความเป็นไปได้ที่ซ้ำกันของการใช้ IDisposable กับ destructor ใน C # คืออะไร
ผู้ใช้

คำตอบ:


121

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

บางประเภทห่อหุ้มทรัพยากรที่ใช้แล้วทิ้งในลักษณะที่ใช้งานง่ายและกำจัดทิ้งในการดำเนินการเพียงครั้งเดียว การใช้งานทั่วไปมักจะเป็นแบบนี้: เปิดอ่านหรือเขียนปิด (ทิ้ง) มันเข้ากันได้ดีกับusingโครงสร้าง

คนอื่นยากกว่านิดหน่อย WaitEventHandlesสำหรับอินสแตนซ์จะไม่ถูกใช้เช่นนี้เนื่องจากใช้เพื่อส่งสัญญาณจากเธรดหนึ่งไปยังเธรดอื่น คำถามจะกลายเป็นใครควรเรียกDisposeสิ่งเหล่านี้ ในฐานะที่เป็นประเภทการป้องกันเช่นนี้จะใช้Finalizeวิธีการซึ่งทำให้มั่นใจได้ว่าทรัพยากรจะถูกกำจัดเมื่อไม่มีการอ้างอิงอีกต่อไปโดยแอปพลิเคชัน


60
ฉันไม่เข้าใจคำตอบที่ได้รับการอนุมัตินี้ ฉันยังต้องการทราบความแตกต่าง มันคืออะไร?
อิสมาเอล

22
@ Ismael: สถานการณ์ที่ยิ่งใหญ่ที่สุดที่Finalizeอาจพิสูจน์ได้คือเมื่อมีวัตถุจำนวนมากที่สนใจที่จะให้ทรัพยากรมีชีวิตอยู่ แต่ไม่มีวิธีการใดที่วัตถุที่หยุดสนใจทรัพยากรสามารถค้นหาได้ว่าเป็น อันสุดท้าย. ในกรณีเช่นนี้Finalizeโดยปกติจะยิงเมื่อไม่มีใครสนใจวัตถุ ช่วงเวลาที่หลวมของFinalizeน่ากลัวสำหรับทรัพยากรที่ไม่สามารถเข้ากันได้เช่นไฟล์และล็อค แต่อาจโอเคสำหรับทรัพยากรที่ใช้งานได้
supercat

13
+1 ถึง supercat สำหรับคำใหม่ (สำหรับฉัน) ที่ยอดเยี่ยม บริบททำให้มันชัดเจน แต่ในกรณีของพวกเราที่เหลือนี่คือสิ่งที่ wikipedia พูดว่า: "Fungibility เป็นสมบัติของสินค้าที่ดีหรือสินค้าที่แต่ละหน่วยมีความสามารถในการทดแทนซึ่งกันและกันเช่นน้ำมันดิบหวาน บริษัท , พันธบัตร, โลหะมีค่าหรือสกุลเงิน "
Jon Coombs

5
@JonCoombs: ที่สวยมากขวาแม้ว่ามันอาจจะเป็นที่น่าสังเกตว่าคำว่า "ทรัพยากรทดแทนได้" ถูกนำไปใช้กับสิ่งที่ทดแทนกันได้อย่างอิสระจนกว่าพวกเขาจะได้มาและกลายเป็นที่ทดแทนได้อย่างอิสระอีกครั้งหลังจากที่ปล่อยหรือถูกทอดทิ้ง หากระบบมีกลุ่มของวัตถุล็อคและรหัสที่ได้มาซึ่งเชื่อมโยงกับเอนทิตีบางอย่างตราบใดที่ทุกคนถือเป็นการอ้างอิงถึงล็อคนั้นเพื่อวัตถุประสงค์ในการเชื่อมโยงกับเอนทิตีนั้นล็อคนั้นอาจไม่ถูกแทนที่ด้วย อื่น ๆ. หากรหัสทั้งหมดที่ใส่ใจเกี่ยวกับกิจการที่ได้รับการปกป้องทิ้งการล็อค ...
supercat

... จากนั้นจะกลายเป็นสิ่งที่สามารถทดแทนได้อย่างอิสระอีกครั้งจนกว่าจะถึงเวลาที่เกี่ยวข้องกับเอนทิตีอื่น ๆ
supercat

135

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

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

การปฏิบัติที่เป็นมาตรฐานในการดำเนินการIDisposableและDisposeเพื่อให้คุณสามารถใช้วัตถุของคุณในusingงบ using(var foo = new MyObject()) { }เช่น และในรอบสุดท้ายของคุณคุณโทรDisposeในกรณีที่รหัสการโทรลืมที่จะกำจัดคุณ


17
คุณต้องระมัดระวังเกี่ยวกับการโทรออกจากการใช้งานขั้นสุดท้าย - การกำจัดอาจกำจัดทรัพยากรที่มีการจัดการซึ่งคุณไม่ต้องการสัมผัสจากเครื่องมือสุดท้ายของคุณเนื่องจากอาจได้รับการสรุปแล้ว
itowlson

6
@itowlson: การตรวจสอบค่า null รวมกับการสันนิษฐานว่าวัตถุสามารถถูกกำจัดได้สองครั้ง (ด้วยการเรียกใช้ครั้งที่สองที่ไม่ทำอะไรเลย) ควรจะดีพอ
ซามูเอล

7
รูปแบบ IDisposition มาตรฐานและการนำไปใช้งานแบบซ่อนเร้นของ Dispose (bool) เพื่อจัดการกับการกำจัดส่วนประกอบที่มีการจัดการซึ่งเป็นทางเลือกนั้นดูเหมือนว่าจะตอบสนองต่อปัญหาดังกล่าว
Brody

ดูเหมือนไม่มีเหตุผลที่จะใช้เมธอด destructor (~ MyClass ()) และมักจะนำไปใช้และเรียกเมธอด Dispose () หรือฉันผิด ใครช่วยยกตัวอย่างให้ฉันบ้างเมื่อควรนำมาใช้ทั้งคู่
dpelisek

66

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

ในฐานะผู้ใช้วัตถุคุณมักจะใช้ Dispose จบคือสำหรับ GC

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



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

2
@JCoombs: Disposeเป็นสิ่งที่ดีและการใช้อย่างถูกต้องนั้นเป็นเรื่องง่าย Finalizeเป็นสิ่งที่ชั่วร้ายและการนำไปใช้อย่างถูกต้องนั้นเป็นเรื่องยาก เหนือสิ่งอื่นใดเนื่องจาก GC จะรับรองว่าไม่มีตัวตนของวัตถุที่จะ "รีไซเคิล" ตราบใดที่การอ้างอิงถึงวัตถุนั้นมีอยู่มันง่ายต่อการทำความสะอาดDisposableวัตถุจำนวนมากซึ่งบางส่วนอาจได้รับการทำความสะอาดแล้วคือ ไม่มีปัญหา; การอ้างอิงไปยังวัตถุที่Disposeถูกเรียกไปแล้วจะยังคงเป็นการอ้างอิงไปยังวัตถุที่Disposeถูกเรียกไปแล้ว
supercat

2
@JCoombs: ทรัพยากรที่ไม่มีการจัดการโดยทั่วไปจะไม่มีการรับประกันดังกล่าว หากวัตถุFredเป็นเจ้าของหมายเลขอ้างอิงไฟล์ # 42 และปิดระบบอาจแนบหมายเลขเดียวกันนั้นไปยังหมายเลขอ้างอิงไฟล์บางไฟล์ซึ่งมอบให้แก่เอนทิตีอื่น ๆ ในกรณีนั้นหมายเลขอ้างอิงไฟล์ # 42 จะไม่อ้างอิงถึงไฟล์ปิดของ Fred แต่เป็นไฟล์ที่ใช้งานโดยนิติบุคคลอื่นนั้น สำหรับความFredพยายามที่จะปิดหมายเลขอ้างอิง # 42 อีกครั้งจะเป็นหายนะ การพยายามติดตาม 100% อย่างน่าเชื่อถือว่าวัตถุที่ไม่มีการจัดการหนึ่งตัวที่ยังไม่ได้เปิดใช้นั้นสามารถใช้งานได้หรือไม่ การพยายามติดตามวัตถุหลาย ๆ ชิ้นนั้นยากกว่ามาก
supercat

2
@JCoombs: ถ้าไม่มีการจัดการทรัพยากรทุกจะอยู่ในวัตถุเสื้อคลุมของตัวเองที่ไม่ทำอะไรเลย แต่ควบคุมอายุการใช้งานของมันแล้วรหัสนอกซึ่งไม่ทราบว่าทรัพยากรที่ได้รับการปล่อยตัวออกมา แต่รู้ว่ามันควรจะเป็นถ้ามันไม่ได้อยู่แล้วสามารถขอให้วัตถุห่อหุ้มปล่อยอย่างปลอดภัยได้ วัตถุที่ห่อหุ้มจะรู้ว่ามันได้ทำและสามารถดำเนินการหรือละเว้นการร้องขอ ความจริงที่ว่า GC รับประกันว่าการอ้างอิงไปยัง wrapper จะเป็นการอ้างอิงที่ถูกต้องไปยัง wrapper นั้นเป็นการรับประกันที่มีประโยชน์มาก
supercat

43

สรุป

  • finalizers ควรจะเป็นprotectedไม่ได้publicหรือprivateเพื่อให้วิธีการที่ไม่สามารถเรียกว่าจากโค้ดของโปรแกรมโดยตรงและในเวลาเดียวกันก็สามารถโทรไปที่base.Finalizeวิธีการ
  • Finalizers ควรปล่อยทรัพยากรที่ไม่มีการจัดการเท่านั้น
  • เฟรมเวิร์กไม่รับประกันว่า finalizer จะดำเนินการเลยในทุกอินสแตนซ์ที่กำหนด
  • อย่าจัดสรรหน่วยความจำใน finalizers หรือเรียกวิธีเสมือนจาก finalizers
  • หลีกเลี่ยงการซิงโครไนซ์และเพิ่มข้อยกเว้นที่ไม่สามารถจัดการได้ในขั้นตอนสุดท้าย
  • ลำดับการดำเนินการของ finalizers นั้นไม่ได้กำหนดไว้ - กล่าวอีกนัยหนึ่งคุณไม่สามารถพึ่งพาวัตถุอื่นที่ยังคงใช้งานได้ภายในเครื่องมือสุดท้ายของคุณ
  • อย่ากำหนดขั้นสุดท้ายสำหรับประเภทค่า
  • อย่าสร้าง destructors ที่ว่างเปล่า กล่าวอีกนัยหนึ่งคุณไม่ควรกำหนด destructor อย่างชัดเจนเว้นแต่ชั้นเรียนของคุณต้องล้างทรัพยากรที่ไม่มีการจัดการและถ้าคุณกำหนดอย่างใดอย่างหนึ่งก็ควรทำงานบางอย่าง หากภายหลังคุณไม่จำเป็นต้องล้างทรัพยากรที่ไม่มีการจัดการใน destructor อีกต่อไปให้ลบออกทั้งหมด

ทิ้ง

  • นำไปใช้IDisposableกับทุกประเภทที่มี finalizer
  • ตรวจสอบให้แน่ใจว่าวัตถุนั้นไม่สามารถใช้งานได้หลังจากทำการเรียกใช้Disposeเมธอด กล่าวอีกนัยหนึ่งให้หลีกเลี่ยงการใช้วัตถุหลังจากที่Disposeมีการเรียกใช้เมธอดนั้น
  • โทรDisposeทุกIDisposableประเภทเมื่อคุณทำเสร็จแล้ว
  • อนุญาตให้Disposeเรียกได้หลายครั้งโดยไม่เพิ่มข้อผิดพลาด
  • หยุดการเรียกใช้ในภายหลังไปยัง finalizer จากภายในDisposeเมธอดโดยใช้GC.SuppressFinalizeเมธอด
  • หลีกเลี่ยงการสร้างประเภทค่าที่ใช้แล้วทิ้ง
  • หลีกเลี่ยงการโยนข้อยกเว้นจากภายในDisposeวิธีการ

รูปแบบการกำจัด / สรุป

  • Microsoft แนะนำให้คุณใช้ทั้งDisposeและFinalizeเมื่อทำงานกับทรัพยากรที่ไม่มีการจัดการ การFinalizeใช้งานจะทำงานและทรัพยากรจะยังคงถูกปล่อยออกมาเมื่อวัตถุถูกรวบรวมขยะแม้ว่านักพัฒนาละเลยที่จะเรียกDisposeวิธีการอย่างชัดเจน
  • ล้างทรัพยากรที่ไม่มีการจัดการในFinalizeวิธีการเช่นเดียวกับDisposeวิธีการ นอกจากนี้เรียกDisposeวิธีการสำหรับวัตถุ. NET ใด ๆ ที่คุณมีเป็นส่วนประกอบภายในคลาสนั้น (มีทรัพยากรที่ไม่มีการจัดการในฐานะสมาชิกของพวกเขา) จากDisposeวิธีการ

17
ฉันอ่านคำตอบเดียวกันนี้ทุกที่และฉันก็ยังไม่เข้าใจว่าอะไรคือจุดประสงค์ของแต่ละคน ฉันอ่านกฎหลังกฎเท่านั้นไม่มีอะไรเพิ่มเติม
อิสมาเอล

@Ismael: และผู้แต่งไม่ได้เพิ่มอะไรเลยยกเว้นการคัดลอกและวางข้อความจาก MSDN
Tarik

@ tarik ฉันได้เรียนรู้แล้ว ฉันมีความคิด "สัญญา" ในครั้งนั้นที่ฉันถามสิ่งนี้
Ismael

31

จบการทำงานถูกเรียกโดย GC เมื่อวัตถุนี้ไม่ได้ใช้งานอีกต่อไป

การกำจัดเป็นเพียงวิธีปกติที่ผู้ใช้ของคลาสนี้สามารถโทรเพื่อปล่อยทรัพยากรใด ๆ

หากผู้ใช้ลืมที่จะโทรออกและถ้าชั้นมีการใช้งานเสร็จแล้ว GC จะทำให้แน่ใจว่ามันได้รับการเรียก


3
คำตอบที่สะอาดที่สุดเท่าที่เคยมีมา
dariogriffo

19

มีปุ่มบางอย่างเกี่ยวกับหนังสือ MCSD Certification Toolkit (สอบ 70-483) pag 193:

destructor ≈ (เกือบเท่ากับ)base.Finalize() , destructor จะถูกแปลงเป็นเวอร์ชันแทนที่ของวิธีการสุดท้ายที่เรียกใช้งานรหัสของผู้ทำลายและจากนั้นเรียกวิธีการสุดท้ายของชั้นฐาน ถ้าอย่างนั้นมันก็ไม่สามารถยับยั้งได้โดยสิ้นเชิงคุณไม่สามารถรู้ได้ว่าเมื่อไหร่จะถูกเรียกเพราะขึ้นอยู่กับ GC

ถ้าคลาสไม่มีรีซอร์สที่ถูกจัดการและไม่มีรีซอร์สที่ไม่มีการจัดการคลาสนั้นไม่ควรนำไปใช้IDisposableหรือมี destructor

ถ้าคลาสมีเพียงทรัพยากรที่มีการจัดการควรนำมาใช้IDisposableแต่ไม่ควรมี destructor (เมื่อ destructor ดำเนินการคุณจะไม่สามารถมั่นใจได้ว่าวัตถุที่มีการจัดการยังคงอยู่ดังนั้นคุณจึงไม่สามารถเรียกDispose()วิธีการของพวกเขาได้)

หากคลาสมีทรัพยากรที่ไม่ได้รับการจัดการก็ต้องดำเนินการIDisposableและต้องการตัวทำลายระบบในกรณีที่โปรแกรมไม่ได้เรียกDispose()ใช้

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

Dispose()ควรเป็นอิสระทั้งการจัดการทรัพยากรและไม่มีการจัดการ

destructor ควรฟรีเฉพาะทรัพยากรที่ไม่มีการจัดการ เมื่อตัวทำลายดำเนินการคุณจะไม่สามารถมั่นใจได้ว่าวัตถุที่มีการจัดการยังคงอยู่ดังนั้นคุณจึงไม่สามารถเรียกใช้วิธีการกำจัดทิ้งได้ นี้จะได้รับโดยใช้บัญญัติprotected void Dispose(bool disposing)รูปแบบที่เป็นทรัพยากรที่มีการจัดการเพียงเป็นอิสระ (จำหน่าย) disposing == trueเมื่อ

หลังจากพ้นทรัพยากรแล้วDispose()ควรเรียกGC.SuppressFinalizeเพื่อให้วัตถุสามารถข้ามคิวการสรุปสุดท้ายได้

ตัวอย่างของการนำไปใช้งานสำหรับคลาสที่มีทรัพยากรที่ไม่มีการจัดการและมีการจัดการ:

using System;

class DisposableClass : IDisposable
{
    // A name to keep track of the object.
    public string Name = "";

    // Free managed and unmanaged resources.
    public void Dispose()
    {
        FreeResources(true);

        // We don't need the destructor because
        // our resources are already freed.
        GC.SuppressFinalize(this);
    }

    // Destructor to clean up unmanaged resources
    // but not managed resources.
    ~DisposableClass()
    {
        FreeResources(false);
    }

    // Keep track if whether resources are already freed.
    private bool ResourcesAreFreed = false;

    // Free resources.
    private void FreeResources(bool freeManagedResources)
    {
        Console.WriteLine(Name + ": FreeResources");
        if (!ResourcesAreFreed)
        {
            // Dispose of managed resources if appropriate.
            if (freeManagedResources)
            {
                // Dispose of managed resources here.
                Console.WriteLine(Name + ": Dispose of managed resources");
            }

            // Dispose of unmanaged resources here.
            Console.WriteLine(Name + ": Dispose of unmanaged resources");

            // Remember that we have disposed of resources.
            ResourcesAreFreed = true;
        }
    }
}

2
นี่คือคำตอบที่ดี! แต่ฉันคิดว่านี่เป็นสิ่งที่ผิด: "ผู้ทำลายระบบควรเรียก GC.SuppressFinalize" สาธารณะไม่ควรทิ้งวิธี () เรียก GC.SuppressFinalize แทน? โปรดดู: docs.microsoft.com/en-us/dotnet/api/…การเรียกใช้วิธีนี้จะป้องกันไม่ให้ตัวรวบรวมข้อมูลขยะเรียกใช้ Object.Finalize (ซึ่งถูกเขียนทับโดย destructor)
Ewa

7

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

มีอะไรอีกมากมายให้เรียนรู้เกี่ยวกับหัวเรื่องของGarbage Collectionแต่นั่นเป็นการเริ่มต้น


5
ฉันค่อนข้างมั่นใจว่าแอปพลิเคชัน C # มากกว่า 1% ใช้ฐานข้อมูล: ที่ซึ่งคุณต้องกังวลเกี่ยวกับสิ่งที่ IDisposable SQL
ซามูเอล

1
นอกจากนี้คุณควรใช้ IDisposable ถ้าคุณใส่แค็ปซูล IDisposables ซึ่งอาจครอบคลุมอีก 1%
Darren Clark

@Samuel: ฉันไม่เห็นว่าฐานข้อมูลเกี่ยวข้องกับมัน หากคุณกำลังพูดถึงการปิดการเชื่อมต่อก็ไม่เป็นไร แต่นั่นเป็นเรื่องที่แตกต่าง คุณไม่จำเป็นต้องกำจัดวัตถุเพื่อปิดการเชื่อมต่อในเวลาที่เหมาะสม
JP Alioto

1
@JP: แต่รูปแบบการใช้ (... ) ทำให้ง่ายต่อการจัดการ
Brody

2
เห็นด้วย แต่นั่นคือประเด็น รูปแบบการใช้จะซ่อนสายที่จะทิ้งให้คุณ
JP Alioto

6

Finalizer นั้นใช้สำหรับการล้างข้อมูลโดยปริยาย - คุณควรใช้สิ่งนี้เมื่อใดก็ตามที่คลาสจัดการทรัพยากรที่จะต้องล้างอย่างแน่นอนมิฉะนั้นคุณจะรั่วไหลที่จับ / หน่วยความจำ ฯลฯ ...

การใช้งาน finalizer อย่างถูกต้องนั้นเป็นเรื่องยากและควรหลีกเลี่ยงทุกที่ที่เป็นไปได้SafeHandleคลาส (avaialble ใน. Net v2.0 และสูงกว่า) ในตอนนี้หมายความว่าคุณแทบจะไม่ต้องใช้ finalizer อีกเลย

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

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

ดูDG ปรับปรุง: ทิ้ง, Finalization และการจัดการทรัพยากรสำหรับสิ่งที่ฉันคิดว่าจะเป็นชุดที่ดีที่สุดและสมบูรณ์ที่สุดของคำแนะนำเกี่ยวกับ finalizers IDisposableและ


3

สรุปคือ -

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

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

สำหรับคลาสที่ใช้ทรัพยากรที่ไม่มีการจัดการวิธีปฏิบัติที่ดีที่สุดคือการกำหนดทั้งเมธอด Dispose () และ Finalizer - เพื่อใช้เป็นทางเลือกในกรณีที่นักพัฒนาลืมที่จะกำจัดออบเจ็กต์อย่างชัดเจน ทั้งสองสามารถใช้วิธีการที่ใช้ร่วมกันเพื่อล้างทรัพยากรที่มีการจัดการและไม่มีการจัดการ: -

class ClassWithDisposeAndFinalize : IDisposable
{
    // Used to determine if Dispose() has already been called, so that the finalizer
    // knows if it needs to clean up unmanaged resources.
     private bool disposed = false;

     public void Dispose()
     {
       // Call our shared helper method.
       // Specifying "true" signifies that the object user triggered the cleanup.
          CleanUp(true);

       // Now suppress finalization to make sure that the Finalize method 
       // doesn't attempt to clean up unmanaged resources.
          GC.SuppressFinalize(this);
     }
     private void CleanUp(bool disposing)
     {
        // Be sure we have not already been disposed!
        if (!this.disposed)
        {
             // If disposing equals true i.e. if disposed explicitly, dispose all 
             // managed resources.
            if (disposing)
            {
             // Dispose managed resources.
            }
             // Clean up unmanaged resources here.
        }
        disposed = true;
      }

      // the below is called the destructor or Finalizer
     ~ClassWithDisposeAndFinalize()
     {
        // Call our shared helper method.
        // Specifying "false" signifies that the GC triggered the cleanup.
        CleanUp(false);
     }

2

ตัวอย่างที่ดีที่สุดที่ฉันรู้

 public abstract class DisposableType: IDisposable
  {
    bool disposed = false;

    ~DisposableType()
    {
      if (!disposed) 
      {
        disposed = true;
        Dispose(false);
      }
    }

    public void Dispose()
    {
      if (!disposed) 
      {
        disposed = true;
        Dispose(true);
        GC.SuppressFinalize(this);
      }
    }

    public void Close()
    {
      Dispose();
    }

    protected virtual void Dispose(bool disposing)
    {
      if (disposing) 
      {
        // managed objects
      }
      // unmanaged objects and resources
    }
  }

2

ความแตกต่างระหว่างจบและวิธีการกำจัดใน C #

GC เรียกใช้วิธีการสุดท้ายเพื่อเรียกคืนทรัพยากรที่ไม่มีการจัดการ (เช่นการทำงานของไฟล์, windows api, การเชื่อมต่อเครือข่าย, การเชื่อมต่อฐานข้อมูล) แต่เวลาจะไม่คงที่เมื่อ GC จะเรียกมันว่า มันถูกเรียกโดยปริยายโดย GC มันหมายความว่าเราไม่มีการควบคุมระดับต่ำ

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


1

อินสแตนซ์ของคลาสมักจะห่อหุ้มการควบคุมทรัพยากรที่ไม่ได้รับการจัดการโดยรันไทม์เช่นตัวจัดการหน้าต่าง (HWND), การเชื่อมต่อฐานข้อมูลและอื่น ๆ ดังนั้นคุณควรให้ทั้งวิธีที่ชัดเจนและโดยนัยในการเพิ่มทรัพยากรเหล่านั้น จัดเตรียมการควบคุมโดยนัยโดยใช้วิธีการสุดท้ายที่ได้รับการป้องกันบนวัตถุ (ไวยากรณ์ destructor ใน C # และ Managed Extensions สำหรับ C ++) ตัวรวบรวมขยะเรียกวิธีนี้ในบางจุดหลังจากที่ไม่มีการอ้างอิงที่ถูกต้องไปยังวัตถุอีกต่อไป ในบางกรณีคุณอาจต้องการให้โปรแกรมเมอร์ใช้วัตถุที่มีความสามารถในการเผยแพร่ทรัพยากรภายนอกเหล่านี้อย่างชัดเจนก่อนที่ตัวรวบรวมขยะจะทำให้วัตถุเป็นอิสระ หากทรัพยากรภายนอกมีน้อยหรือมีราคาแพงประสิทธิภาพที่ดีขึ้นสามารถทำได้หากโปรแกรมเมอร์ปล่อยทรัพยากรอย่างชัดเจนเมื่อไม่มีการใช้งานอีกต่อไป เพื่อให้การควบคุมที่ชัดเจนใช้วิธีการกำจัดที่ได้รับจากส่วนติดต่อ IDisposable ผู้บริโภคของวัตถุควรเรียกวิธีนี้เมื่อใช้วัตถุเสร็จแล้ว สามารถกำจัดทิ้งแม้ว่าการอ้างอิงอื่น ๆ ไปยังวัตถุยังมีชีวิตอยู่

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


1

ความแตกต่างที่สำคัญระหว่างการกำจัดและการทำให้จบคือ:

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

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

ฉันเองจะเขียนตรรกะการทำลายส่วนใหญ่ในการกำจัด หวังว่านี่จะช่วยขจัดความสับสน


-1

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


กำจัดปลดปล่อยทรัพยากรทันที จบอาจจะหรือไม่อาจปลดปล่อยทรัพยากรด้วยระดับของความทันเวลาใด ๆ
supercat

1
อ่าเขาน่าจะหมายความว่า "ต้องตรวจสอบ GC วัตถุสองครั้งก่อนที่หน่วยความจำจะถูกเรียกคืน" อ่านรายละเอียดเพิ่มเติมได้ที่นี่: ericlippert.com/2015/05/18/ …
aeroson

-4

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

สำหรับคำถามที่สองดีกว่าอ่านก่อนนี้การ ใช้อินเตอร์เฟซ IDisposable ที่เหมาะสมซึ่งอ้างว่า

มันเป็นทางเลือกของคุณ! แต่เลือกทิ้ง

กล่าวอีกนัยหนึ่ง: GC รู้เพียงเกี่ยวกับ finalizer (ถ้ามีรู้จักกันในชื่อ destructor ของ Microsoft) รหัสที่ดีจะพยายามล้างข้อมูลจากทั้งสอง (finalizer และ Dispose)

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