Что такое интерфейс IDisposable в C# и для чего он нужен?

«Что такое интерфейс IDisposable в C# и для чего он нужен?» — вопрос из категории Управление памятью, который задают на 47% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

IDisposable — это интерфейс, который предоставляет механизм для детерминированного освобождения неуправляемых ресурсов (файловые дескрипторы, сетевое соединение, дескрипторы окон, подключения к БД), которые не контролируются сборщиком мусора (GC).

Почему это важно: Сборщик мусора C# управляет только памятью, выделенной в управляемой куче. Ресурсы вроде файлов или сокетов существуют вне этой кучи, и если их не освобождать явно, это приведёт к утечкам ресурсов.

Базовая реализация паттерна Disposable:

public class DatabaseConnection : IDisposable
{
    private SqlConnection _connection; // Пример неуправляемого ресурса
    private bool _disposed = false; // Флаг для защиты от повторного вызова

    public DatabaseConnection(string connectionString)
    {
        _connection = new SqlConnection(connectionString);
        _connection.Open();
    }

    // Публичный метод, который должен вызывать клиентский код
    public void Dispose()
    {
        Dispose(true); // Освобождаем управляемые и неуправляемые ресурсы
        GC.SuppressFinalize(this); // Отменяем вызов финализатора, т.к. очистка уже выполнена
    }

    // Защищённый виртуальный метод, где происходит реальная очистка
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // Освобождаем управляемые ресурсы (те, у которых тоже есть Dispose)
            _connection?.Close();
            _connection?.Dispose();
        }

        // Здесь освобождаем неуправляемые ресурсы (например, через P/Invoke)
        // В данном примере SqlConnection — управляемая обёртка, поэтому тут может быть пусто.

        _disposed = true;
    }

    // Финализатор (деструктор) — резервный механизм на случай, если Dispose не был вызван.
    ~DatabaseConnection()
    {
        Dispose(false); // Вызываем только для очистки неуправляемых ресурсов
    }
}

Использование:

Лучший способ работы с IDisposable — конструкция using, которая гарантирует вызов Dispose() даже при возникновении исключения.

// using гарантирует вызов connection.Dispose() при выходе из блока
using (var connection = new DatabaseConnection("Server=myServer;Database=myDB;"))
{
    // Работа с подключением
    // При любом исходе (нормальном завершении или исключении) Dispose() будет вызван.
}
// Здесь ресурс connection уже освобождён.

Ключевой момент: IDisposable — это не про управление памятью, а про управление жизненным циклом внешних ресурсов. Все классы .NET, работающие с файлами (FileStream), сетью (HttpClient), графикой (Bitmap), реализуют этот интерфейс.