Ответ
Да, работа с неуправляемыми ресурсами требует особого внимания к управлению памятью. Вот ключевые подходы:
1. Паттерн Disposable:
public class FileProcessor : IDisposable
{
private IntPtr _handle; // WinAPI handle
private bool _disposed = false;
public FileProcessor(string path)
{
_handle = NativeMethods.CreateFile(path, ...);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Освобождение управляемых ресурсов
}
// Освобождение неуправляемых ресурсов
if (_handle != IntPtr.Zero)
{
NativeMethods.CloseHandle(_handle);
_handle = IntPtr.Zero;
}
_disposed = true;
}
}
~FileProcessor() => Dispose(false);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
// Использование
using (var processor = new FileProcessor("data.bin"))
{
processor.Process();
} // Автоматический вызов Dispose()
2. SafeHandle для безопасной работы:
public sealed class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
3. Распространенные сценарии:
- COM-объекты через
Marshal.ReleaseComObject() - GDI+ ресурсы (Bitmap, Graphics)
- Сетевые сокеты и дескрипторы файлов
- Выделенная неуправляемая память через
Marshal.AllocHGlobal()
4. Best practices:
- Всегда реализовывать
IDisposableдля классов с неуправляемыми ресурсами - Использовать
usingдля гарантированного освобождения - Добавлять финализатор только если ресурс критически важен
- Проверять
IntPtr.Zeroперед освобождением