IDisposable Pattern (Dispose Pattern) เป็นมาตรฐานสำหรับปล่อยทรัพยากร Unmanaged Resources (File Handles, Database Connections, Network Streams, COM Objects) อย่างถูกต้องและปลอดภัย
เมื่อไหร่ต้อง Implement IDisposable?
- คลาสครอบ Unmanaged Resources โดยตรง (IntPtr, SafeHandle)
- คลาสมี Field ที่เป็น IDisposable (Managed Resource ที่ครอบ Unmanaged)
- Base Class Implement IDisposable (Derived ต้อง Override)
Standard Dispose Pattern (Full Pattern)
public class ResourceWrapper : IDisposable
{
// 1. Unmanaged Resource (เช่น Handle, Pointer)
private IntPtr _unmanagedHandle;
// 2. Managed Resource ที่ Implement IDisposable
private readonly FileStream _fileStream;
// 3. Flag ตรวจสอบ Dispose แล้วหรือยัง
private bool _disposed = false;
public ResourceWrapper(string filePath)
{
_fileStream = File.OpenRead(filePath);
_unmanagedHandle = _fileStream.SafeFileHandle.DangerousGetHandle(); // ตัวอย่าง
}
// 4. Public Dispose() - เรียกได้จากภายนอก
public void Dispose()
{
Dispose(true); // 5. เรียก Overload ที่ทำงานจริง
GC.SuppressFinalize(this); // 6. บอก GC ว่าไม่ต้องเรียก Finalizer
}
// 7. Protected Virtual - ให้ Derived Class Override ได้
protected virtual void Dispose(bool disposing)
{
if (_disposed) return; // Guard Clause
if (disposing)
{
// 8. Dispose Managed Resources (เรียก Dispose ของ Fields)
_fileStream?.Dispose();
// managedResource?.Dispose();
}
// 9. Free Unmanaged Resources (เสมอ - ทั้ง Disposing และ Finalizing)
if (_unmanagedHandle != IntPtr.Zero)
{
// CloseHandle(_unmanagedHandle); // P/Invoke
_unmanagedHandle = IntPtr.Zero;
}
_disposed = true;
}
// 10. Finalizer (Destructor) - เรียกโดย GC ถ้าลืม Dispose()
~ResourceWrapper()
{
Dispose(false); // false = ไม่ Dispose Managed Resources (อาจถูก GC แล้ว)
}
// ใช้งาน Resource
public void ReadData()
{
if (_disposed) throw new ObjectDisposedException(nameof(ResourceWrapper));
// ...
}
}
การใช้งานกับ using Statement (แนะนำ)
// using เรียก Dispose() อัตโนมัติ แม้จะมี Exception
using (var wrapper = new ResourceWrapper("data.txt"))
{
wrapper.ReadData();
} // Dispose() ถูกเรียกที่นี่
รูปแบบย่อสำหรับ Managed Resources เท่านั้น (No Unmanaged)
public class SimpleWrapper : IDisposable
{
private readonly DbContext _context;
private bool _disposed;
public SimpleWrapper() => _context = new DbContext();
public void Dispose()
{
if (!_disposed)
{
_context?.Dispose();
_disposed = true;
}
}
// ไม่ต้องมี Finalizer หากไม่มี Unmanaged Resources
}
Best Practices
- ใช้
SafeHandle(CriticalHandle) แทน IntPtr สำหรับ Unmanaged Handles - ทำให้
Dispose()เป็น Idempotent (เรียกซ้ำได้ไม่ Error) - โยน
ObjectDisposedExceptionหากใช้หลัง Dispose - Derived Class: Override
Dispose(bool)เรียกbase.Dispose(disposing)ท้ายสุด - ใน .NET Core/5+: ใช้
IAsyncDisposableสำหรับ Async Cleanup
ตัวอย่าง Derived Class
public class DerivedWrapper : ResourceWrapper
{
private readonly MemoryStream _memoryStream;
public DerivedWrapper(string filePath) : base(filePath)
{
_memoryStream = new MemoryStream();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_memoryStream?.Dispose(); // Dispose Own Managed Resources
}
base.Dispose(disposing); // เรียก Base สุดท้าย
}
}