Перфолента.NET. ООП. Конструируем класс. Завершители и Деструкторы
Конструируем класс. Завершители и Деструкторы.
Из этой статьи Вы узнаете, как в языке Перфолента.Net правильно завершать работу создаваемых Вами классов.
Когда Вы закончили использование Объекта, не забудьте запрограммировать правильную очистку и освобождение ресурсов!
В языке программирования Перфолента, как и в других языках, основанных на технологии .Net, очисткой памяти от не нужных программе объектов занимается сборщик мусора. Это позволяет программисту не писать никакого дополнительного кода для освобождения памяти от созданных вашей программой объектов после окончания их использования.
Однако, есть случаи, когда сборщик мусора не может освободить занятую программой память или захваченные в монопольное владение устройства компьютера. Это происходит тогда, когда память и устройства выделяются вашей программе не исполнительной средой .Net (CLR) поверх которой работает программа, а операционной системой. В этом случае ваша программа должна сама освободить захваченные ресурсы, когда они ей больше не нужны.
Замечание: Что бы Ваша программа была стабильной, быстрой и не мешала работе операционной системы и других программ, необходимо выполнять очистку памяти и освобождение устройств, которые не может очистить и освободить сборщик мусора .NET! Для этого Вы должны хорошо понимать откуда берутся объекты в Вашей программе и какие ресурсы они используют.
Содержание
Термины
Управляемые ресурсы – память и устройства, которые выделяются программе исполнительной средой .Net (CLR).
Не управляемые ресурсы – память и устройства, которые выделяются программе операционной системой или программой написанной на языке программирования не использующем исполнительную среду .Net (CLR).
Сборщик мусора – часть исполнительной среды .Net (CLR), которая занимается очисткой и освобождением управляемых ресурсов.
Завершитель – метод объекта, который должен быть вызван оператором ВызватьЗавершитель для очистки управляемых ресурсов (т.е. созданных с помощью NET), занимаемых объектом. Завершитель будет вызван сборщиком мусора только в случае, если:
- он не был вызван ранее
- и на объект больше нет ссылок (т.е. он неиспользуется)
- и у объекта есть метод Деструктор.
Деструктор – метод объекта, который будет вызван сборщиком мусора или оператором ВызватьЗавершитель для очистки НЕ управляемых ресурсов ((т.е. созданных средствами ОС или другой программы не использующей NET)), занимаемых объектом.
Как понять, управляемый ресурс вы используете или не управляемый?
Если все классы и структуры, используемые в вашей программе, Вы написали сами, используя только типы данных имеющиеся в языке Перфолента, то все созданные программой объекты находятся под управлением исполняющей среды .Net (CLR), а значит являются управляемыми ресурсами.
Если Вы, для создания объектов, использовали классы и структуры из чужих библиотек (в том числе из стандартной библиотеки языка Перфолента), то Вам следует обратиться к документации и выяснить какие ресурсы используются и необходимо ли вручную вызывать методы их очистки и освобождения. Даже такой объект как Шрифт является управляемой обёрткой над неуправляемым объектом, т.к. шрифты являются объектами операционной системы.
Если Вы использовали в программе вызовы Windows API, объекты COM или любые библиотеки, написанные на языках, не имеющих отношения к .Net (например, C, C++, Rust и.т.д.), то скорее всего Вы использовали неуправляемые ресурсы и должны позаботиться об их очистке и освобождении.
Если Вы понимаете, что используемый вами объект работает с физическим устройством – диском, сетью, принтером, USB, PCI, COM, LPT устройством и т.д., то скорее всего он использует неуправляемые ресурсы и требует очистки или освобождения по окончанию работы с ним, даже если сам объект является управляемым.
Погружаемся в .Net
Язык Перфолента основан на .Net, поэтому внутренняя реализация методов Завершитель и Деструктор соответствует рекомендованным правилам согласно документации .Net Framework.
Класс или структура могут реализовать интерфейс System.IDisposable, в котором имеется единственный метод Dispose, который программист может вручную вызвать для очистки и освобождения занятых объектом ресурсов, когда объект больше не нужен. Наличие метода Dispose является характерным признаком того, что объект нуждается в очистке и освобождении ресурсов.
А что будет, если программист забудет вызвать Dispose? Тогда сборщик мусора постарается вызвать метод Finalize, если такой метод у объекта есть. С помощью этих 2-х методов в любых классах и структурах .Net реализуется вся логика очистки и освобождения занятых объектом ресурсов.
Язык программирования Перфолента автоматически создаёт реализацию интерфейса System.IDisposable и методы Dispose и Finalize, если у класса определен один из методов Завершитель или Деструктор. Хотя, возможность реализовать их вручную так же имеется.
Очистка и освобождение управляемых ресурсов
После всего выше сказанного, у Вас могло сложиться впечатление, что управляемые ресурсы очищать и освобождать не надо, т.к. с этим прекрасно справляется сборщик мусора. Это верно, но с технической стороны Вы могли бы ему помочь. Обнуляя ссылки на объекты, хранящиеся в полях, и вызывая их завершители, Вы помогаете сборщику мусора, оставляя ему меньше работы.
Метод Завершитель
Для освобождения управляемых ресурсов в программе на языке Перфолента используется специальный метод класса Завершитель:
Класс МойКласс
Поле МояОгромнаяСтрока тип Строка Поле МояЧиталкаФайлов тип ЧиталкаФайлов //…. Завершитель МояОгромнаяСтрока = Неопределено ВызватьЗавершитель МояЧиталкаФайлов МояЧиталкаФайлов = Неопределено КонецЗавершителя
КонецКласса
В этом примере в качестве управляемых ресурсов представлены 2 объекта хранящиеся в полях МояОгромнаяСтрока и МояЧиталкаФайлов. В строке предположительно находится очень большой текст, занимающий память, а тип ЧиталкаФайлов определен во внешней библиотеке (написанной на одном из .Net языков) и судя по названию имеет дело с файловой системой, т.е. нуждается в вызове завершителя для освобождения открытых файлов. Если тип ЧиталкаФайлов реализует интерфейс System.IDisposable, то значит разработчик этого типа прямо указал на то, что вы должны вызвать завершитель этого объекта, когда он больше не нужен.
В данном случае, сборщик мусора прекрасно справится с очисткой сам, т.к. оба объекта управляемые, однако, если Ваша программа с большой скоростью генерирует большое количество подобных объектов, то возможно будет хорошим решением сразу освобождать память и файлы занятую теми из них, которые больше не нужны.
Важно!!! Если в полях класса НЕ хранятся объекты, занимающие много памяти или требующие вызова завершителя, то создавать метод Завершитель НЕ надо!!!
Обычные строки, числа, даты и другие типы, не требующие завершителя, будут удалены сборщиком мусора, как только на них не останется ссылок из других объектов программы.
Старайтесь перехватить все возможные ошибки, которые могут возникнуть в процессе работы метода Завершитель.
Совет: Старайтесь не хранить в полях объекты, которым требуется вызов завершителя! Храните их только в переменных в пределах метода! Будет идеально, если Вы сможете создавать и завершать объекты в пределах одного метода:
Класс МойКласс
//…. Метод МояРабота //создадим объекты, использующие управляемые ресурсы Перем МояОгромнаяСтрока тип Строка = ПрочитатьОгромныйТекст() Перем МояЧиталкаФайлов тип ЧиталкаФайлов = Новый ПрочитатьФайлы(ПутьККаталогу) //…. //тут выполняется полезная работа //…. //освобождаем управляемые ресурсы МояОгромнаяСтрока = Неопределено ВызватьЗавершитель МояЧиталкаФайлов МояЧиталкаФайлов = Неопределено КонецМетода //обошлись без метода Завершитель…
КонецКласса
Оператор ВызватьЗавершитель.
В приведенных выше примерах, был использован оператор ВызватьЗавершитель, который вызывает завершитель объекта, хранящегося в поле или в переменной МояЧиталкаФайлов.
Оператор можно использовать для освобождения и очистки любых объектов, у которых есть хотя бы один из методов Завершитель или Деструктор (для программ, написанных на языке Перфолента), или которые реализуют интерфейс System.IDisposable (для программ, написанных на других языках).
Оператор можно использовать для вызова завершителей и деструкторов сразу нескольких объектов.
ВызватьЗавершитель Объект1, Объект2, ОбъектН
У объекта есть завершитель, если:
- объект написан на языке Перфолента и у него есть хотя бы один из методов – Завершитель или Деструктор;
- объект реализует интерфейс System.IDisposable и у него есть метод Dispose;
Если объект требующий вызова завершителя создаётся и прекращает свою работу в пределах одного метода, то вместо оператора ВызватьЗавершитель при окончании работы с объектом, используйте оператор Использовать при создании объекта (или оператор Исп, сокращенную версию оператора Использовать). Подробнее про оператор Использовать можно прочитать в статье «Оператор Использовать - гарантированное завершение объекта».
Очистка и освобождение НЕ управляемых ресурсов.
В отличие от управляемых, НЕ управляемые ресурсы освобождать обязательно!!! С ними не справится сборщик мусора! Нарушение этого правила может привести к проблемам в операционной системе или нарушить доступность оперативной памяти или физических устройств компьютера для других программ.
Метод Деструктор.
Для освобождения НЕ управляемых ресурсов в программе на языке Перфолента используется специальный метод класса Деструктор:
Класс МойКласс
//это поле хранит контекст устройства, который был получен в конструкторе //объекта с помощью вызова функции CreateDC из Windows API Поле HDC тип Цел32 //…. Деструктор //надо обязательно удалить контекст устройства!!! DeleteDC(HDC) КонецДеструктора
КонецКласса
Так как контекст устройства был получен у операционной системы, то он однозначно является НЕ управляемым ресурсом. При его создании операционная система заняла память для размещения сопутствующих данных, а также выполнила перенаправление обращений к устройству таким образом, чтобы именно ваша программа могла вести запись (или чтение) в устройство. Если Вы не удалите выданный программе контекст устройства, когда он больше не нужен, то операционная система об этом не узнает, и будет по-прежнему думать, что он нужен, возможно блокируя доступ к нему другим программам.
Если в классе существует метод Деструктор, то Вы можете использовать оператор ВызватьЗавершитель, чтобы вызвать Деструктор вручную.
Наличие в классе метода Деструктор позволит сборщику мусора выполнить работу по удалению захваченного контекста устройства, в том случае, если завершитель не был вызван вручную.
Важно!!! Если в полях класса НЕ хранятся данные представляющие НЕ управляемые ресурсы, то создавать метод Деструктор НЕ надо!!!
Старайтесь перехватить все возможные ошибки, которые могут возникнуть в процессе работы метода Деструктор.
Завершители и Деструкторы в Классе.
В классе методы Завершитель и Деструктор могут быть реализованы как по отдельности, так и вместе. Это зависит только от того, какие ресурсы необходимо освобождать.
Для конкретного экземпляра объекта методы Завершитель и Деструктор будут вызваны один раз не зависимо от того, сколько раз для объекта был использован оператор ВызватьЗавершитель и сработал ли сборщик мусора.
Если в классе есть хотя бы один из методов Завершитель или Деструктор, то компилятор автоматически добавит в класс поле БылВызванЗавершитель типа Булево, чтобы отследить факт вызова этих методов и не вызывать их повторно. Вы можете использовать это поле в других методах класса, чтобы понимать, можно ли продолжать использовать объект или для него уже был вызван завершитель и объект более не пригоден к использованию.
Завершители и Деструкторы в Структуре.
В языке Перфолента.Net Вы НЕ можете создать методы Завершитель и Деструктор в структуре. И хотя Вы по-прежнему можете реализовать интерфейс System.IDisposable вручную, делать это крайне не рекомендуется. Одна из причин состоит в том, что при присваивании переменной или при передаче в параметры метода, структура копируется вместе с содержимым всех полей. Поэтому в программе может неявно присутствовать несколько копий структуры, в том числе в скрытых временных переменных, созданных компилятором. Вы можете легко запутаться и вызвать метод Dispose для уже освобожденных ресурсов.
Так же учтите, что сборщик мусора игнорирует метод Finalize для структур и по этой причине его реализация бесполезна.
Если структура хранит ресурсы требующие очистки или освобождения, то подумайте о том, чтобы заменить её на класс или включить в класс-обёртку в качестве поля.
Сборщик мусора вызывает только метод Деструктор.
Если дело дошло до того, что объект попал для удаления к сборщику мусора, то будет вызван только метод Деструктор, если он есть. Метод Завершитель сборщиком мусора вызван не будет, т.к. управляемые ресурсы сборщик мусора удаляет сам.
Вывод: Для освобождения управляемых ресурсов в классе необходимо создать метод Завершитель, а для освобождения НЕ управляемых ресурсов в классе необходимо создать метод Деструктор.