ใน C # อะไรคือความแตกต่างระหว่างตัวทำลายและเมธอด Finalize ในคลาส?


98

อะไรคือความแตกต่างระหว่างตัวทำลายและเมธอด Finalize ในคลาส

ฉันเพิ่งค้นพบว่า Visual Studio 2008 พิจารณาตัวทำลายที่มีความหมายเหมือนกันกับวิธี Finalize ซึ่งหมายความว่า Visual Studio จะไม่ยอมให้คุณกำหนดทั้งสองวิธีพร้อมกันในคลาส

ตัวอย่างเช่นส่วนของรหัสต่อไปนี้:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

ให้ข้อผิดพลาดต่อไปนี้ในการเรียกให้เสร็จสิ้นในตัวทำลาย:

การเรียกไม่ชัดเจนระหว่างวิธีการหรือคุณสมบัติต่อไปนี้: 'TestFinalize. ~ TestFinalize ()' และ 'TestFinalize.Finalize ()'

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

พิมพ์ 'ManagementConcepts.Service.TestFinalize' แล้วกำหนดสมาชิกที่เรียกว่า 'Finalize' ด้วยประเภทพารามิเตอร์เดียวกัน

คำตอบ:


68

ตัวทำลายใน C # แทนที่System.Object.Finalizeเมธอด คุณต้องใช้ไวยากรณ์ destructor จึงจะทำได้ การลบล้างด้วยตนเองFinalizeจะทำให้คุณมีข้อความแสดงข้อผิดพลาด

โดยพื้นฐานแล้วสิ่งที่คุณพยายามทำกับFinalizeการประกาศเมธอดของคุณคือการซ่อนเมธอดของคลาสพื้นฐาน มันจะทำให้คอมไพลเลอร์ออกคำเตือนซึ่งสามารถปิดเสียงได้โดยใช้newตัวปรับแต่ง (ถ้ามันจะทำงาน) สิ่งที่สำคัญที่จะต้องทราบที่นี่เป็นที่ที่คุณไม่สามารถทั้งสองoverrideและประกาศnewสมาชิกที่มีชื่อเหมือนกันในเวลาเดียวกันเพื่อให้มีทั้งเตาเผาและFinalizeวิธีการที่จะส่งผลให้เกิดข้อผิดพลาด ( แต่คุณสามารถแม้จะไม่แนะนำประกาศpublic new void Finalize()วิธีการถ้า คุณไม่ได้ประกาศตัวทำลายล้าง)


71

Wikipedia มีการอภิปรายที่ดีเกี่ยวกับความแตกต่างระหว่าง Finalizer และDestructorในบทความFinalizer

C # ไม่มีตัวทำลาย "ที่แท้จริง" จริงๆ ไวยากรณ์คล้ายกับตัวทำลาย C ++ แต่จริงๆแล้วมันเป็นโปรแกรมสุดท้าย คุณเขียนอย่างถูกต้องในส่วนแรกของตัวอย่าง:

~ClassName() { }

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

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

การใช้คำว่า "destructor" ของ C ++ ของ Microsoft ทำให้เข้าใจผิดเนื่องจากใน C ++ จะถูกดำเนินการในเธรดเดียวกันทันทีที่วัตถุถูกลบหรือโผล่ออกจากสแต็กในขณะที่ใน C # จะดำเนินการกับเธรดที่แยกจากกันในเวลาอื่น


ฉันจะเถียงว่าความแตกต่างระหว่างตัวทำลายและตัวสุดท้ายเป็นสิ่งสำคัญที่ต้องทำ อย่างไรก็ตามมีเพียงผู้ที่สนใจเกี่ยวกับสิ่งที่เกิดขึ้นภายใต้ประทุนเท่านั้นที่จะสนใจเกี่ยวกับความแตกต่างดังกล่าว
Kyle Baran

1
นอกจากนี้โปรดทราบว่า ECMA-334 ได้ยกเลิก "destructor" และ "finalizer" อย่างชัดเจนอย่างเป็นทางการเมื่อนานมาแล้ว ฉันไม่รู้ว่าทำไม MS ยังคงยืนยันในข้อกำหนดที่ทำให้เข้าใจผิดในข้อกำหนดของพวกเขา
FrankHB

อย่างน้อยจากการทำงานกับ Mono C # จะถูกจำลองตาม C ++ จริง ๆ และวัตถุ C # ดั้งเดิมส่วนใหญ่เป็นวัตถุ C ++ วิธีที่คอมไพเลอร์ที่คอมไพล์ Mono ทำงานเป็นตัวกำหนดว่าวัตถุ C ++ เหล่านั้นถูกทำลายอย่างไรและในทำนองเดียวกันการสรุปวัตถุ C # จะแพร่กระจายไปยัง C ++ และเรียกตัวทำลายเหล่านั้นอย่างไร ความแตกต่างนั้นสมเหตุสมผลภายใต้ประทุน แต่ก็ยังใช้ไม่ได้กับ C #
Kenzi

20

พบที่นี่: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Destructor

    เป็นวิธีการพิเศษที่มีรหัสล้างสำหรับวัตถุ คุณไม่สามารถเรียกมันอย่างชัดเจนในรหัสของคุณได้เนื่องจาก GC เรียกโดยปริยาย ใน C # มีชื่อเดียวกันกับชื่อคลาสที่นำหน้าด้วย~เครื่องหมาย ชอบ-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }
    

    ใน VB.NET ตัวทำลายจะถูกนำไปใช้โดยการแทนที่เมธอด Finalize ของคลาส System.Object

  2. ทิ้ง

    สิ่งเหล่านี้เหมือนกับวิธีการอื่น ๆ ในคลาสและสามารถเรียกได้อย่างชัดเจน แต่มีจุดประสงค์พิเศษในการล้างวัตถุ ในวิธีการกำจัดเราเขียนโค้ดทำความสะอาดสำหรับวัตถุ เป็นสิ่งสำคัญที่เราจะต้องปลดปล่อยแหล่งที่มาที่ไม่มีการจัดการทั้งหมดในเมธอดการกำจัดเช่นการเชื่อมต่อฐานข้อมูลไฟล์ ฯลฯ คลาสที่ใช้เมธอดการกำจัดควรใช้อินเทอร์เฟซ IDisposable เมธอด Dispose ควรเรียกเมธอด GC.SuppressFinalize สำหรับอ็อบเจ็กต์ที่กำลังกำจัดหาก คลาสมีตัวทำลายล้างเนื่องจากได้ดำเนินการล้างอ็อบเจ็กต์ไปแล้วจึงไม่จำเป็นที่ตัวรวบรวมขยะจะเรียกใช้เมธอด Finalize ของอ็อบเจ็กต์ อ้างอิง: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. สรุป

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

    หมายเหตุ:ใน C # ไม่สามารถแทนที่เมธอด Finalize ได้ดังนั้นคุณต้องใช้ destructor ซึ่งการใช้งานภายในจะแทนที่เมธอด Finalize ใน MSIL แต่ใน VB.NET เมธอด Finalize สามารถแทนที่ได้เนื่องจากรองรับเมธอด destructor

ปรับปรุง: น่าสนใจด้ายกึ่งเกี่ยวโยงที่นี่


1
You should only implement a Finalize method to clean up unmanaged resources: คุณใส่ไว้ใน Finalize เหมือนกันกับ Dispose?
hqt

@hqt: กรณีที่ควรใช้งานDisposeจำนวนมากเกินกว่าที่ควรใช้โปรแกรมFinalizer นำไปใช้Disposeหากมีแนวโน้มว่าอินสแตนซ์ของคลาสหรือคลาสที่ได้รับจะเป็นสิ่งสุดท้ายที่จะเป็นเจ้าของทรัพยากรที่ไม่มีการจัดการโดยตรงหรือเป็นเจ้าของสิ่งสุดท้ายโดยตรงเพื่อเป็นเจ้าของทรัพยากรที่ไม่มีการจัดการโดยตรงหรือเป็นเจ้าของสิ่งสุดท้ายโดยตรงเพื่อเป็นเจ้าของโดยตรง ฯลฯ ใช้เฉพาะFinalizeสำหรับการล้างทรัพยากรถ้าคลาส <i> โดยตรง </i> เป็นเจ้าของทรัพยากรที่ไม่มีการจัดการ <i> และแทบจะไม่มีอะไรอื่นเลย </i> - สถานการณ์ที่แคบกว่า
supercat

@hqt: หากคลาสใดคลาสหนึ่งจะเป็นเจ้าของทรัพยากรที่ไม่มีการจัดการโดยตรงและยังมีการอ้างอิงถึงอ็อบเจ็กต์อื่น ๆ ทรัพยากรที่ไม่มีการจัดการโดยทั่วไปควรแบ่งออกเป็นคลาสที่สามารถสรุปได้ (ซึ่งไม่ควรมีการอ้างอิงที่ชัดเจนถึงสิ่งอื่นใด) หมายถึงคลาส ซึ่งมีการอ้างอิงถึงวัตถุอื่น ๆ จะเป็นเจ้าของ "สิ่งที่เป็นเจ้าของทรัพยากรที่ไม่มีการจัดการโดยตรง" แทนที่จะเป็นเจ้าของทรัพยากรด้วยตัวเองและไม่จำเป็นต้องมีขั้นสุดท้าย
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.