Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Руководство_по_C#.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
10.01 Mб
Скачать

13.7Финализируемые и высвобождаемые типы

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

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

Ниже приведена версия класса MyResourceWrapper, которая предусматривает выполнение и финализации и освобождения:

// Сложный упаковщик ресурсов.

public class MyResourceWrapper : IDisposable

{

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

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

~MyResourceWrapper()

{

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

// ресурсов. Метод Dispose() НЕ должен вызываться

// ни для каких управляемых объектов.

}

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

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

public void Dispose()

{

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

// и вызов Dispose() для остальных высвобождаемых объектов.

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

// поэтому далее она подавляется.

GC.SuppressFinalize(this);

}

}

Здесь важно обратить внимание на то, что метод Dispose() был модифицирован так, чтобы вызывать метод GC.SuppressFinalize(). Этот метод информирует CLR-среду о том, что вызывать деструктор при подвергании данного объекта сборке мусора больше не требуется, поскольку неуправляемые ресурсы уже были освобождены посредством логики Dispose().

Формализованный шаблон очистки

В текущей реализации MyResourceWrapper работает довольно хорошо, но все равно еще остается несколько небольших недочетов. Во-первых, методам Finalize() и Dispose() требуется освобождать одни и те же неуправляемые ресурсы, а это чревато дублированием кода, которое может существенно усложнить его сопровождение. Поэтому в идеале не помешало бы определить приватную вспомогательную функцию, которая могла бы вызываться в любом из этих методов.

Во-вторых, нелишне позаботиться о том, чтобы метод Finalize() не пытался избавиться от любых управляемых объектов, а метод Dispose() — наоборот, обязательно это делал. И, наконец, в-третьих, не помешало бы позаботиться о том, чтобы пользователь объекта мог спокойно вызывать метод Dispose() множество раз без получения ошибки. В настоящий момент в методе Dispose() никаких подобных мер предосторожностей пока не предусмотрено.

Для решения подобных вопросов с дизайном в Microsoft создали формальный шаблон очистки, который позволяет достичь оптимального баланса между надежностью, удобством в обслуживании и производительностью. Ниже приведена окончательная версия MyResourceWrapper, в которой применяется упомянутый формальный шаблон:

public class MyResourceWrapper : IDisposable

(

// Используется для выяснения того, вызывался ли уже метод Dispose()

private bool disposed = false;

public void Dispose ()

{

// Вызов вспомогательного метода.

// Значение true указывает на то, что очистка

// была инициирована пользователем объекта.

Cleanup(true);

// Подавление финализации.

GC.SuppressFinalize (this);

}

private void Cleanup(bool disposing)

{

// Проверка, выполнялась ли очистка,

if (!this.disposed)

{

// Если disposing равно true, должно осуществляться

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

if (disposing)

{

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

}

// Очистка неуправляемых ресурсов.

}

disposed = true;

}

~MyResourceWrapper()

{

// Вызов вспомогательного метода.

// Значение false указывает на то, что

// очистка была инициирована сборщиком мусора.

Cleanup(false);

}

}