ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание
Шрифт:
• Правило. Необходимость переопределения Finalize может возникать только тогда, когда класс C# использует неуправляемые ресурсы посредством PInvoke или при решении сложных задач взаимодействия с COM-объектами (обычно с применением типа System.Runtime.InteropServices.Marshal).
Замечание. В главе 3 уже отмечалось, что не допускается переопредешть Finalize с типами структуры. Теперь это совершенно ясно, поскольку структуры являются типами, характеризуемыми значениями, и они не размещаются в динамической памяти.
Переопределение System.Object.Finalize
В тех редких случаях, когда воздается C#-класс,
Чтобы в пользовательском типе класса переопределить метод Finalize, вы должны использовать в C# другой синтаксис деструктора, напоминающий синтаксис деструктора в C++. Причина в том, что при обработке деструктора компилятором C# в метод Finalize автоматически добавляются необходимые элементы инфраструктуры (что будет продемонстрировано чуть позже).
Вот пример пользовательского деструктора для MyResourceWrapper, который при вызове генерирует системный сигнал. Ясно, что это сделано исключительно для примера. Настоящий деструктор должен только освобождать неуправляемые ресурсы, а не взаимодействовать с членами других управляемых o6ъeктов, поскольку вы не можете гарантировать, что эти объекты будут существовать в тот момент, когда сборщик мусора вызовет ваш метод Finalize.
Если рассмотреть этот деструктор с помощью ildasm.exe, вы увидите, что компилятор добавляет программный код контроля ошибок. Программный код вашего метода Finаlize помещается в рамки блока try. Это делается для выявления операторов, которые во время выполнения могут сгенерировать ошибку (что формально называется исключительной ситуацией или исключением). Соответствующий блок finally гарантирует, что метод Finalize класса будет выполнен независимо от исключений, которые могут возникать в рамках try. Формальности структурированной обработки исключений будут рассмотрены в следующей главе, а пока что проанализируйте следующее CIL-представление деструктора для C#-класса MyResourceWrapper.
При
тестировании типа MyResourceWrapper вы обнаружите, что завершение работы приложения сопровождается системным звуковым сигналом, поскольку среда CLR автоматически вызывает деструкторы объектов при освобождении доменов приложений.Исходный код. Проект SimpleFinalize размещен в подкаталоге, соответствующем главе 5.
Детали процесса финализации
Чтобы не делать лишней работы, следует помнить о том, что целью метода Finalize является гарантия освобождения неуправляемых ресурсов .NET-объекта при сборке мусора. Поэтому при cоздании типа, не использующего неуправляемые элементы (а такая ситуация оказывается вполне типичней), от финализации будет мало пользы. На самом деле, при разработке своих типов вы должны избегать использования метода Finalize по той очень простой причине, что финализация требует времени.
При размещений объекта в управляемой динамической памяти среда выполнения автоматически определяет, поддерживает ли этот объект пользовательский метод Finalize. Если указанный метод поддерживается, то объект обозначается, как требующий финализации, а указатель на этот объект сохраняется во внутренней очереди, которую называют очередью финализации. Очередь финализации представляет собой таблицу, поддерживаемую сборщиком мусора и содержащую все объекты, для которых перед удалением из динамической памяти требуется финализация.
Когда с точки зрения сборщика мусора приходит время удалить объект из памяти, проверяются элементы очереди финализации, и соответствующий объект копируется из динамической памяти в другую управляемую структуру, которую вызывают таблицей элементов, доступных для финализации. В этот момент создается отдельный поток, задачей которого является вызов метода Finalize при следующей сборке мусора для каждого объекта из таблицы элементов, доступных для финализации. С учетом этого становится. ясно, что для окончательного уничтожения объекта потребуется как минимум две процедуры сборки мусора.