Tóm tắt nhanh
| Đặc điểm | Finalize (Destructor) | Dispose |
|---|---|---|
| Mục đích | Dọn dẹp unmanaged resources khi GC collect | Dọn dẹp managed + unmanaged resources ngay lập tức |
| Gọi bởi | Garbage Collector (non-deterministic) | Developer (deterministic) |
| Thời điểm | Không xác định (khi GC run) | Ngay khi không cần object |
| Interface | Không (override Object.Finalize) | IDisposable |
| Performance | Tốn kém (object lên Gen 2, finalizer queue) | Nhẹ, kiểm soát được |
| Exception handling | Khô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.
- 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
- Kế thừa
IDisposable. - Field
bool _disposedguard. public void Dispose()gọiDispose(true)+GC.SuppressFinalize(this).protected virtual void Dispose(bool disposing)- logic chính.- Nếu có unmanaged resources → thêm
~Finalizer() { Dispose(false); }. - Sealed class? Finalizer không cần
virtual. - Base class có Dispose? Override
Dispose(bool)và gọibase.Dispose(disposing). - Throw
ObjectDisposedExceptiontrong 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)