Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Khác biệt giữa Finalize và Dispose trong C# (IDisposable Pattern)

Tóm tắt nhanh

Đặc điểmFinalize (Destructor)Dispose
Mục đíchDọn dẹp unmanaged resources khi GC collectDọn dẹp managed + unmanaged resources ngay lập tức
Gọi bởiGarbage Collector (non-deterministic)Developer (deterministic)
Thời điểmKhông xác định (khi GC run)Ngay khi không cần object
InterfaceKhông (override Object.Finalize)IDisposable
PerformanceTốn kém (object lên Gen 2, finalizer queue)Nhẹ, kiểm soát được
Exception handlingKhông throw (crash process)Có thể throw, xử lý bình thường

Finalize (Destructor)


public class ResourceHolder 
{
    private IntPtr _handle; // Unmanaged resource
    
    // Destructor syntax (C#) = Finalize method
    ~ResourceHolder() 
    {
        // Chỉ dọn dẹp unmanaged resources
        // KHÔNG truy cập managed objects (có thể đã finalized)
        ReleaseHandle(_handle);
        _handle = IntPtr.Zero;
    }
    
    // P/Invoke example
    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    private static extern bool CloseHandle(IntPtr hObject);
    
    private void ReleaseHandle(IntPtr handle) 
    {
        if (handle != IntPtr.Zero) 
        {
            CloseHandle(handle);
        }
    }
}

Biên dịch thành IL:


protected override void Finalize() 
{
    try 
    {
        // Cleanup statements...
    }
    finally 
    {
        base.Finalize(); // Luôn gọi base.Finalize()
    }
}

Dispose Pattern (Full Implementation)


public class ResourceHolder : IDisposable 
{
    private IntPtr _handle;      // Unmanaged resource
    private MemoryStream _stream; // Managed resource
    private bool _disposed = false;
    
    public ResourceHolder() 
    {
        _handle = AllocateHandle();
        _stream = new MemoryStream();
    }
    
    // 1. Public Dispose() - gọi bởi user code
    public void Dispose() 
    {
        Dispose(true);
        GC.SuppressFinalize(this); // Quan trọng: báo GC không cần gọi Finalize
    }
    
    // 2. Protected virtual Dispose(bool) - core logic
    protected virtual void Dispose(bool disposing) 
    {
        if (_disposed) return;
        
        if (disposing) 
        {
            // Dọn dẹp MANAGED resources (chỉ khi gọi từ Dispose())
            _stream?.Dispose();
            _stream = null;
        }
        
        // Luôn dọn dẹp UNMANAGED resources
        ReleaseHandle(_handle);
        _handle = IntPtr.Zero;
        
        _disposed = true;
    }
    
    // 3. Finalizer - chỉ dọn dẹp unmanaged
    ~ResourceHolder() 
    {
        Dispose(false); // disposing = false
    }
    
    // Helper method
    public void DoWork() 
    {
        if (_disposed) throw new ObjectDisposedException(nameof(ResourceHolder));
        // Work...
    }
}

Sử dụng với using statement (C# 8+ using declaration)


// C# 8+ - using declaration (scope-based)
using var resource = new ResourceHolder();
resource.DoWork();
// resource.Dispose() tự động gọi ở cuối scope

// Trước C# 8 - using block
using (var resource = new ResourceHolder()) 
{
    resource.DoWork();
} // Dispose() gọi ở đây

Microsoft Recommendation: Implement BOTH

Best Practice: Khi có unmanaged resources → implement cả Dispose VÀ Finalize.
- Dispose: cho developer dọn dẹp ngay (deterministic).
- Finalize: safety net nếu developer quên gọi Dispose.

SafeHandle - Cách hiện đại thay thế Finalize

Từ .NET 2.0+, khuyến nghị dùng SafeHandle thay vì raw IntPtr + Finalize:


using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;

public class SafeResource : IDisposable 
{
    private SafeFileHandle _handle;
    
    public SafeResource(string path) 
    {
        _handle = File.OpenHandle(path); // Trả về SafeFileHandle
    }
    
    public void Dispose() 
    {
        _handle?.Dispose(); // SafeHandle tự handle finalization
    }
}

Checklist implement IDisposable

  1. Kế thừa IDisposable.
  2. Field bool _disposed guard.
  3. public void Dispose() gọi Dispose(true) + GC.SuppressFinalize(this).
  4. protected virtual void Dispose(bool disposing) - logic chính.
  5. Nếu có unmanaged resources → thêm ~Finalizer() { Dispose(false); }.
  6. Sealed class? Finalizer không cần virtual.
  7. Base class có Dispose? Override Dispose(bool) và gọi base.Dispose(disposing).
  8. Throw ObjectDisposedException trong public methods sau khi disposed.

Common Unmanaged Resources

  • File handles, Socket handles, Registry handles
  • Database connections (ADO.NET)
  • GDI objects (Bitmap, Font, Brush)
  • COM objects (Marshal.ReleaseComObject)
  • P/Invoke allocated memory (Marshal.AllocHGlobal)