ทำไมบางคนถึงใช้Finalize
วิธีนี้มากกว่าDispose
วิธี?
ในสถานการณ์ใดบ้างที่คุณจะใช้Finalize
วิธีนี้เหนือDispose
วิธีการและในทางกลับกัน
ทำไมบางคนถึงใช้Finalize
วิธีนี้มากกว่าDispose
วิธี?
ในสถานการณ์ใดบ้างที่คุณจะใช้Finalize
วิธีนี้เหนือDispose
วิธีการและในทางกลับกัน
คำตอบ:
คนอื่น ๆ ได้ครอบคลุมถึงความแตกต่างระหว่างDispose
และFinalize
(btw Finalize
วิธีการยังคงเรียกว่า destructor ในข้อมูลจำเพาะภาษา) ดังนั้นฉันจะเพิ่มเล็กน้อยเกี่ยวกับสถานการณ์ที่Finalize
วิธีการมีประโยชน์
บางประเภทห่อหุ้มทรัพยากรที่ใช้แล้วทิ้งในลักษณะที่ใช้งานง่ายและกำจัดทิ้งในการดำเนินการเพียงครั้งเดียว การใช้งานทั่วไปมักจะเป็นแบบนี้: เปิดอ่านหรือเขียนปิด (ทิ้ง) มันเข้ากันได้ดีกับusing
โครงสร้าง
คนอื่นยากกว่านิดหน่อย WaitEventHandles
สำหรับอินสแตนซ์จะไม่ถูกใช้เช่นนี้เนื่องจากใช้เพื่อส่งสัญญาณจากเธรดหนึ่งไปยังเธรดอื่น คำถามจะกลายเป็นใครควรเรียกDispose
สิ่งเหล่านี้ ในฐานะที่เป็นประเภทการป้องกันเช่นนี้จะใช้Finalize
วิธีการซึ่งทำให้มั่นใจได้ว่าทรัพยากรจะถูกกำจัดเมื่อไม่มีการอ้างอิงอีกต่อไปโดยแอปพลิเคชัน
Finalize
อาจพิสูจน์ได้คือเมื่อมีวัตถุจำนวนมากที่สนใจที่จะให้ทรัพยากรมีชีวิตอยู่ แต่ไม่มีวิธีการใดที่วัตถุที่หยุดสนใจทรัพยากรสามารถค้นหาได้ว่าเป็น อันสุดท้าย. ในกรณีเช่นนี้Finalize
โดยปกติจะยิงเมื่อไม่มีใครสนใจวัตถุ ช่วงเวลาที่หลวมของFinalize
น่ากลัวสำหรับทรัพยากรที่ไม่สามารถเข้ากันได้เช่นไฟล์และล็อค แต่อาจโอเคสำหรับทรัพยากรที่ใช้งานได้
เมธอด finalizer ถูกเรียกเมื่อวัตถุของคุณถูกรวบรวมขยะและคุณไม่รับประกันว่าจะเกิดอะไรขึ้น (คุณสามารถบังคับได้ แต่จะทำให้ประสิทธิภาพลดลง)
Dispose
วิธีในมืออื่น ๆ ที่มีความหมายที่จะถูกเรียกตามรหัสที่สร้างชั้นเรียนของคุณเพื่อให้คุณสามารถทำความสะอาดและปล่อยทรัพยากรใด ๆ ที่คุณได้รับ (ข้อมูลที่ไม่มีการจัดการการเชื่อมต่อฐานข้อมูลที่จับไฟล์ ฯลฯ ) ขณะที่รหัสจะทำด้วย วัตถุของคุณ
การปฏิบัติที่เป็นมาตรฐานในการดำเนินการIDisposable
และDispose
เพื่อให้คุณสามารถใช้วัตถุของคุณในusing
งบ using(var foo = new MyObject()) { }
เช่น และในรอบสุดท้ายของคุณคุณโทรDispose
ในกรณีที่รหัสการโทรลืมที่จะกำจัดคุณ
จบคือวิธีการหนุนหลังเรียกโดยตัวเก็บขยะเมื่อ reclaims วัตถุ การกำจัดเป็นวิธี "การล้างค่าที่กำหนดไว้แล้ว" ซึ่งเรียกโดยแอปพลิเคชันเพื่อปล่อยทรัพยากรดั้งเดิมที่มีค่า (มือจับหน้าต่างการเชื่อมต่อฐานข้อมูล ฯลฯ ) เมื่อไม่ต้องการใช้อีกต่อไปแทนที่จะทิ้งไว้ค้างไว้จนกว่าจะถึงวัตถุ
ในฐานะผู้ใช้วัตถุคุณมักจะใช้ Dispose จบคือสำหรับ GC
ในฐานะผู้ดำเนินการของคลาสหากคุณมีทรัพยากรที่มีการจัดการที่ควรกำจัดคุณต้องใช้ Dispose หากคุณมีทรัพยากรดั้งเดิมคุณใช้ทั้ง Dispose และ Finalize และทั้งสองเรียกวิธีการทั่วไปที่จะปล่อยทรัพยากรดั้งเดิม โดยปกติแล้วสำนวนเหล่านี้จะถูกรวมเข้าด้วยกันด้วยวิธีการกำจัดแบบส่วนตัว (การกำจัดแบบบูล) ซึ่งจะจัดการการโทรด้วยจริงและการโทรสุดท้ายด้วยการเท็จ เมธอดนี้ทำให้รีซอร์สเนทีฟเป็นอิสระเสมอจากนั้นตรวจสอบพารามิเตอร์การทิ้งและถ้าเป็นจริงมันจะกำจัดรีซอร์สที่ถูกจัดการและเรียกใช้ GC.SuppressFinalize
Dispose
เป็นสิ่งที่ดีและการใช้อย่างถูกต้องนั้นเป็นเรื่องง่าย Finalize
เป็นสิ่งที่ชั่วร้ายและการนำไปใช้อย่างถูกต้องนั้นเป็นเรื่องยาก เหนือสิ่งอื่นใดเนื่องจาก GC จะรับรองว่าไม่มีตัวตนของวัตถุที่จะ "รีไซเคิล" ตราบใดที่การอ้างอิงถึงวัตถุนั้นมีอยู่มันง่ายต่อการทำความสะอาดDisposable
วัตถุจำนวนมากซึ่งบางส่วนอาจได้รับการทำความสะอาดแล้วคือ ไม่มีปัญหา; การอ้างอิงไปยังวัตถุที่Dispose
ถูกเรียกไปแล้วจะยังคงเป็นการอ้างอิงไปยังวัตถุที่Dispose
ถูกเรียกไปแล้ว
Fred
เป็นเจ้าของหมายเลขอ้างอิงไฟล์ # 42 และปิดระบบอาจแนบหมายเลขเดียวกันนั้นไปยังหมายเลขอ้างอิงไฟล์บางไฟล์ซึ่งมอบให้แก่เอนทิตีอื่น ๆ ในกรณีนั้นหมายเลขอ้างอิงไฟล์ # 42 จะไม่อ้างอิงถึงไฟล์ปิดของ Fred แต่เป็นไฟล์ที่ใช้งานโดยนิติบุคคลอื่นนั้น สำหรับความFred
พยายามที่จะปิดหมายเลขอ้างอิง # 42 อีกครั้งจะเป็นหายนะ การพยายามติดตาม 100% อย่างน่าเชื่อถือว่าวัตถุที่ไม่มีการจัดการหนึ่งตัวที่ยังไม่ได้เปิดใช้นั้นสามารถใช้งานได้หรือไม่ การพยายามติดตามวัตถุหลาย ๆ ชิ้นนั้นยากกว่ามาก
สรุป
protected
ไม่ได้public
หรือprivate
เพื่อให้วิธีการที่ไม่สามารถเรียกว่าจากโค้ดของโปรแกรมโดยตรงและในเวลาเดียวกันก็สามารถโทรไปที่base.Finalize
วิธีการทิ้ง
IDisposable
กับทุกประเภทที่มี finalizerDispose
เมธอด กล่าวอีกนัยหนึ่งให้หลีกเลี่ยงการใช้วัตถุหลังจากที่Dispose
มีการเรียกใช้เมธอดนั้นDispose
ทุกIDisposable
ประเภทเมื่อคุณทำเสร็จแล้วDispose
เรียกได้หลายครั้งโดยไม่เพิ่มข้อผิดพลาดDispose
เมธอดโดยใช้GC.SuppressFinalize
เมธอดDispose
วิธีการรูปแบบการกำจัด / สรุป
Dispose
และFinalize
เมื่อทำงานกับทรัพยากรที่ไม่มีการจัดการ การFinalize
ใช้งานจะทำงานและทรัพยากรจะยังคงถูกปล่อยออกมาเมื่อวัตถุถูกรวบรวมขยะแม้ว่านักพัฒนาละเลยที่จะเรียกDispose
วิธีการอย่างชัดเจนFinalize
วิธีการเช่นเดียวกับDispose
วิธีการ นอกจากนี้เรียกDispose
วิธีการสำหรับวัตถุ. NET ใด ๆ ที่คุณมีเป็นส่วนประกอบภายในคลาสนั้น (มีทรัพยากรที่ไม่มีการจัดการในฐานะสมาชิกของพวกเขา) จากDispose
วิธีการจบการทำงานถูกเรียกโดย GC เมื่อวัตถุนี้ไม่ได้ใช้งานอีกต่อไป
การกำจัดเป็นเพียงวิธีปกติที่ผู้ใช้ของคลาสนี้สามารถโทรเพื่อปล่อยทรัพยากรใด ๆ
หากผู้ใช้ลืมที่จะโทรออกและถ้าชั้นมีการใช้งานเสร็จแล้ว GC จะทำให้แน่ใจว่ามันได้รับการเรียก
มีปุ่มบางอย่างเกี่ยวกับหนังสือ 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;
}
}
}
99% ของเวลาที่คุณไม่ควรกังวลเกี่ยวกับทั้ง :) แต่ถ้าวัตถุของคุณมีการอ้างอิงถึงทรัพยากรที่ไม่ได้รับการจัดการ (เช่นที่จับหน้าต่าง, ที่จับไฟล์เป็นต้น) คุณจะต้องให้วิธีการสำหรับวัตถุที่มีการจัดการของคุณเพื่อปล่อยทรัพยากรเหล่านั้น ขั้นสุดท้ายให้การควบคุมโดยนัยเกี่ยวกับการปล่อยทรัพยากร มันถูกเรียกโดยตัวเก็บขยะ การกำจัดเป็นวิธีที่จะให้การควบคุมที่ชัดเจนเกี่ยวกับการปล่อยทรัพยากรและสามารถเรียกได้โดยตรง
มีอะไรอีกมากมายให้เรียนรู้เกี่ยวกับหัวเรื่องของGarbage Collectionแต่นั่นเป็นการเริ่มต้น
Finalizer นั้นใช้สำหรับการล้างข้อมูลโดยปริยาย - คุณควรใช้สิ่งนี้เมื่อใดก็ตามที่คลาสจัดการทรัพยากรที่จะต้องล้างอย่างแน่นอนมิฉะนั้นคุณจะรั่วไหลที่จับ / หน่วยความจำ ฯลฯ ...
การใช้งาน finalizer อย่างถูกต้องนั้นเป็นเรื่องยากและควรหลีกเลี่ยงทุกที่ที่เป็นไปได้SafeHandle
คลาส (avaialble ใน. Net v2.0 และสูงกว่า) ในตอนนี้หมายความว่าคุณแทบจะไม่ต้องใช้ finalizer อีกเลย
IDisposable
อินเตอร์เฟซสำหรับการทำความสะอาดอย่างชัดเจนและมีการใช้มากขึ้นทั่วไป - คุณควรใช้วิธีนี้เพื่อให้ผู้ใช้อย่างชัดเจนปล่อยหรือทรัพยากรการทำความสะอาดเมื่อใดก็ตามที่พวกเขาได้เสร็จสิ้นการใช้วัตถุ
โปรดทราบว่าหากคุณมี finalizer คุณควรใช้IDisposable
อินเทอร์เฟซเพื่ออนุญาตให้ผู้ใช้เผยแพร่ทรัพยากรเหล่านั้นอย่างชัดเจนเร็วกว่าพวกเขาหากวัตถุนั้นถูกรวบรวมขยะ
ดูDG ปรับปรุง: ทิ้ง, Finalization และการจัดการทรัพยากรสำหรับสิ่งที่ฉันคิดว่าจะเป็นชุดที่ดีที่สุดและสมบูรณ์ที่สุดของคำแนะนำเกี่ยวกับ finalizers IDisposable
และ
สรุปคือ -
นอกจากนี้ข้อแตกต่างอีกประการคือ - ในการใช้งาน 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);
}
ตัวอย่างที่ดีที่สุดที่ฉันรู้
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
}
}
ความแตกต่างระหว่างจบและวิธีการกำจัดใน C #
GC เรียกใช้วิธีการสุดท้ายเพื่อเรียกคืนทรัพยากรที่ไม่มีการจัดการ (เช่นการทำงานของไฟล์, windows api, การเชื่อมต่อเครือข่าย, การเชื่อมต่อฐานข้อมูล) แต่เวลาจะไม่คงที่เมื่อ GC จะเรียกมันว่า มันถูกเรียกโดยปริยายโดย GC มันหมายความว่าเราไม่มีการควบคุมระดับต่ำ
วิธีการกำจัด: เรามีการควบคุมระดับต่ำในขณะที่เราเรียกมันจากรหัส เราสามารถเรียกคืนทรัพยากรที่ไม่มีการจัดการเมื่อใดก็ตามที่เรารู้สึกว่ามันไม่สามารถใช้งานได้เราสามารถทำได้โดยใช้รูปแบบการเสนอ IDis
อินสแตนซ์ของคลาสมักจะห่อหุ้มการควบคุมทรัพยากรที่ไม่ได้รับการจัดการโดยรันไทม์เช่นตัวจัดการหน้าต่าง (HWND), การเชื่อมต่อฐานข้อมูลและอื่น ๆ ดังนั้นคุณควรให้ทั้งวิธีที่ชัดเจนและโดยนัยในการเพิ่มทรัพยากรเหล่านั้น จัดเตรียมการควบคุมโดยนัยโดยใช้วิธีการสุดท้ายที่ได้รับการป้องกันบนวัตถุ (ไวยากรณ์ destructor ใน C # และ Managed Extensions สำหรับ C ++) ตัวรวบรวมขยะเรียกวิธีนี้ในบางจุดหลังจากที่ไม่มีการอ้างอิงที่ถูกต้องไปยังวัตถุอีกต่อไป ในบางกรณีคุณอาจต้องการให้โปรแกรมเมอร์ใช้วัตถุที่มีความสามารถในการเผยแพร่ทรัพยากรภายนอกเหล่านี้อย่างชัดเจนก่อนที่ตัวรวบรวมขยะจะทำให้วัตถุเป็นอิสระ หากทรัพยากรภายนอกมีน้อยหรือมีราคาแพงประสิทธิภาพที่ดีขึ้นสามารถทำได้หากโปรแกรมเมอร์ปล่อยทรัพยากรอย่างชัดเจนเมื่อไม่มีการใช้งานอีกต่อไป เพื่อให้การควบคุมที่ชัดเจนใช้วิธีการกำจัดที่ได้รับจากส่วนติดต่อ IDisposable ผู้บริโภคของวัตถุควรเรียกวิธีนี้เมื่อใช้วัตถุเสร็จแล้ว สามารถกำจัดทิ้งแม้ว่าการอ้างอิงอื่น ๆ ไปยังวัตถุยังมีชีวิตอยู่
โปรดทราบว่าแม้ว่าคุณจะให้การควบคุมที่ชัดเจนโดยวิธีการกำจัดคุณควรจัดให้มีการล้างข้อมูลโดยนัยโดยใช้วิธีการสุดท้าย ขั้นสุดท้ายให้การสำรองข้อมูลเพื่อป้องกันไม่ให้ทรัพยากรรั่วไหลอย่างถาวรหากโปรแกรมเมอร์ไม่สามารถเรียกใช้การกำจัด
ความแตกต่างที่สำคัญระหว่างการกำจัดและการทำให้จบคือ:
Dispose
มักจะถูกเรียกโดยรหัสของคุณ ทรัพยากรจะถูกปลดปล่อยทันทีเมื่อคุณเรียกใช้ ผู้คนลืมที่จะเรียกวิธีการนี้ดังนั้นusing() {}
คำสั่งจึงถูกคิดค้นขึ้น เมื่อโปรแกรมของคุณเสร็จสิ้นการดำเนินการของรหัสภายใน{}
มันจะเรียกDispose
วิธีการโดยอัตโนมัติ
Finalize
ไม่ได้ถูกเรียกด้วยรหัสของคุณ มีความหมายว่าจะเรียกโดย Garbage Collector (GC) นั่นหมายความว่าทรัพยากรอาจถูกปล่อยให้เป็นอิสระตลอดเวลาในอนาคตเมื่อใดก็ตามที่ GC ตัดสินใจทำเช่นนั้น เมื่อ GC ใช้งานได้มันจะผ่านวิธีการหลายขั้นตอนสุดท้าย หากคุณมีเหตุผลหนักในเรื่องนี้มันจะทำให้กระบวนการช้าลง มันอาจทำให้เกิดปัญหาประสิทธิภาพการทำงานสำหรับโปรแกรมของคุณ ดังนั้นระวังสิ่งที่คุณใส่เข้าไป
ฉันเองจะเขียนตรรกะการทำลายส่วนใหญ่ในการกำจัด หวังว่านี่จะช่วยขจัดความสับสน
ในขณะที่เรารู้ว่าการจัดการและการทำให้เสร็จสิ้นทั้งสองถูกใช้เพื่อปลดปล่อยทรัพยากรที่ไม่มีการจัดการ .. แต่ความแตกต่างคือการจบใช้สองรอบเพื่อปลดปล่อยทรัพยากรซึ่งเป็นการกำจัดใช้หนึ่งรอบ ..
ในการตอบคำถามในส่วนแรกคุณควรให้ตัวอย่างที่ผู้คนใช้วิธีการที่แตกต่างกันสำหรับ class-object เดียวกัน ไม่งั้นมันจะตอบยาก (หรือแม้แต่แปลก)
สำหรับคำถามที่สองดีกว่าอ่านก่อนนี้การ ใช้อินเตอร์เฟซ IDisposable ที่เหมาะสมซึ่งอ้างว่า
มันเป็นทางเลือกของคุณ! แต่เลือกทิ้ง
กล่าวอีกนัยหนึ่ง: GC รู้เพียงเกี่ยวกับ finalizer (ถ้ามีรู้จักกันในชื่อ destructor ของ Microsoft) รหัสที่ดีจะพยายามล้างข้อมูลจากทั้งสอง (finalizer และ Dispose)