Несмотря на автоматическую сборку мусора (GC) в .NET, существуют ресурсы, которые требуют детерминированного освобождения. К ним относятся:
Unmanaged ресурсы:
Managed ресурсы с дорогим освобождением:
Конструкция using
гарантирует вызов Dispose()
даже при возникновении исключения:
using (var resource = new SomeDisposable())
{
// Работа с ресурсом
} // Dispose() вызывается автоматически здесь
Это эквивалентно:
var resource = new SomeDisposable();
try
{
// Работа с ресурсом
}
finally
{
resource?.Dispose();
}
public class Resource : IDisposable
{
private IntPtr _unmanagedResource; // Пример unmanaged ресурса
private Stream _managedResource; // Пример managed ресурса
private bool _disposed = false;
// Публичный метод Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // Отменяет финализацию
}
// Защищенный виртуальный метод
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// Освобождаем managed ресурсы
_managedResource?.Dispose();
}
// Освобождаем unmanaged ресурсы
CloseHandle(_unmanagedResource);
_unmanagedResource = IntPtr.Zero;
_disposed = true;
}
// Финализатор (на случай, если Dispose не вызвали)
```Resource()
{
Dispose(false);
}
}
Двойное освобождение:
_disposed
предотвращает повторное освобождениеРазные сценарии:
disposing == true
: вызов из Dispose()
(можно освобождать managed)disposing == false
: вызов из финализатора (только unmanaged)Финализатор как страховка:
Dispose()
Оптимизация GC:
GC.SuppressFinalize(this)
убирает объект из очереди финализацииusing (var file = File.OpenRead("data.txt"))
using (var reader = new StreamReader(file))
{
string content = reader.ReadToEnd();
} // Оба ресурса будут корректно освобождены
public class DatabaseConnection : IDisposable
{
private SqlConnection _connection;
public DatabaseConnection(string connectionString)
{
_connection = new SqlConnection(connectionString);
_connection.Open();
}
public void Dispose()
{
_connection?.Close();
_connection?.Dispose();
}
}
using
для disposable-объектовIDisposable
для классов, содержащих:
IDisposable
Dispose(bool)
protected virtualvar stream = File.OpenRead("data.txt"); // Никто не вызовет Dispose()
return stream.ReadToEnd(); // Ресурс "повис"
```MyClass() {
Dispose(); // Опасность! Может быть вызван после уничтожения managed объектов
}
Ключевое слово using
и Disposable-паттерн решают критически важную задачу детерминированного освобождения ресурсов в .NET, дополняя автоматическую сборку мусора. Сложность паттерна обусловлена необходимостью корректной работы как с managed, так и с unmanaged ресурсами, а также обработкой всех возможных сценариев использования и освобождения ресурсов.