Ответ
Деструктор (в терминологии C# — финализатор) — это специальный метод класса, который автоматически вызывается сборщиком мусора (GC) перед освобождением памяти, занятой объектом. Его основное и единственное разумное назначение — аварийное освобождение неуправляемых ресурсов, если разработчик забыл вызвать Dispose().
Синтаксис и важные ограничения:
class ResourceHolder
{
// Неуправляемый ресурс (условно, например, дескриптор файла из WinAPI).
private IntPtr _nativeHandle;
// ФИНАЛИЗАТОР (синтаксически похож на деструктор в C++).
~ResourceHolder()
{
// ВАЖНО: Этот код выполнится в отдельном потоке финализации GC.
// Время вызова НЕПРЕДСКАЗУЕМО (когда GC соберёт объект).
if (_nativeHandle != IntPtr.Zero)
{
// Вызов нативной функции для освобождения ресурса.
NativeMethods.CloseHandle(_nativeHandle);
_nativeHandle = IntPtr.Zero;
}
}
}
Почему финализаторы — это крайняя мера?
| Проблема | Следствие |
|---|---|
| Недетерминированность: Вызов зависит от GC. | Ресурс (файл, сокет) может оставаться занятым намного дольше необходимого. |
| Производительность: Объект с финализатором проходит более сложный цикл сборки (попадает в очередь финализации). | Давление на GC, более долгая пауза. |
| Нет гарантии порядка: Нельзя полагаться на финализацию других объектов. | Попытка освободить ресурс, который уже мог быть собран. |
Правильный шаблон: IDisposable
Для детерминированного освобождения ресурсов всегда реализуйте интерфейс IDisposable.
class ProperResourceHolder : IDisposable
{
private IntPtr _nativeHandle;
private bool _disposed = false;
// Публичный метод Dispose для явного освобождения.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // Сообщаем GC, что финализация не нужна.
}
// Защищённая виртуальная логика освобождения.
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Освобождаем управляемые ресурсы (другие IDisposable-объекты).
}
// Освобождаем неуправляемые ресурсы.
if (_nativeHandle != IntPtr.Zero)
{
NativeMethods.CloseHandle(_nativeHandle);
_nativeHandle = IntPtr.Zero;
}
_disposed = true;
}
}
// ФИНАЛИЗАТОР — только как страховка на случай, если Dispose не вызвали.
~ProperResourceHolder()
{
Dispose(false);
}
}
// Использование:
using (var holder = new ProperResourceHolder())
{
// Работа с ресурсом.
} // Dispose() вызовется автоматически здесь, ресурс освободится немедленно.
Вывод: Используйте финализатор только как резервный механизм для освобождения неуправляемых ресурсов. Основной способ — вызов Dispose() через using или вручную.