Создание типов, предусматривающих освобождение ресурсов и финализацию

Создание типов, предусматривающих освобождение ресурсов и финализацию

К этому моменту мы с вами обсудили два различных подхода в построении классов, способных освобождать свои внутренние неуправляемые ресурсы. С одной стороны, можно переопределить System.Object.Finalize(), тогда вы будете уверены в том, что объект непременно освободит ресурсы при сборке мусора, без какого бы то ни было вмешательства пользователя. С другой стороны, можно реализовать IDisposable, что обеспечит пользователю возможность освободить ресурсы после завершения работы с объектом. Однако, если вызывающая сторона "забудет" вызвать Dispose(), неуправляемые ресурсы смогут оставаться в памяти неопределенно долгое время.

Вы можете догадываться, что есть возможность комбинировать оба эти подхода в одном определении класса. Такая комбинации позволит использовать преимущества обеих моделей. Если пользователь объекта не забудет вызвать Dispose(), то с помощью вызова GC.SuppressFinalize() вы можете информировать сборщик мусора о том. что процесс финализации следует отменить. Еcли пользователь объекта забудет вызвать Dispose(), то объект, в конечном счете, подвергнется процедуре финализации при сборке мусора. Так или иначе, внутренние неуправляемые ресурсы объекта будут освобождены. Ниже предлагается очередной вариант MyResourceWrapper, в котором теперь предусмотрены и финализация, и освобождение ресурсов.

// Сложный контейнер ресурсов.

public class MyResourceWrapper: IDisposable {

 // Сборщик мусора вызывает этот метод в том случае, когда

 // пользователь объекта забывает вызвать Dispose().

 ~MyResourceWrapper() {

  // Освобождение внутренних неуправляемых ресурсов.

  // НЕ следует вызывать Dispose() для управляемых объектов.

 }

 // Пользователь объекта вызывает этот метод для того, чтобы

 // как можно быстрее освободить ресурсы.

 public void Dispose() {

  // Освобождение неуправляемых ресурсов.

  // Вызов Dispose() для содержащихся объектов,

  // предусматривающих освобождение ресурсов.

  // Если пользователь вызвал Dispose(), то финализация не нужна.

  GC.SuppressFinalize(this);

 }

}

Обратите внимание на то, что в метод Dispose() здесь добавлен вызов GC.SuppressFinalize(), информирующий среду CLR о том, что теперь при сборке мусора не требуется вызывать деструктор, поскольку неуправляемые ресурсы уже освобождены с помощью программной логики Dispose().