Оператор Использовать - гарантированное завершение объекта

Материал из ТХАБ.РФ
Перейти к: навигация, поиск

Многие объекты так устроены, что в конце работы с ними, их надо условно "выключить", т.е. корректно завершить их работу. Это позволит освободить занятые объектом физические устройства и очистить память компьютера. Если забыть это сделать, то программа или даже компьютер могут перестать нормально работать. Оператор Использовать позволяет гарантированно вызвать метод-завершитель объекта.

В связи с тем, что язык программирования Перфолента.Net построен на базе среды исполнения .Net, в нем существуют операторы и некоторые другие возможности присущие этой среде.

Погрузимся в глубины .Net

Если класс написан на языке Перфолента и использует только типы, определенные в языке, то он относится к коду, управляемому CLR (Common Language Runtime – общеязыковая среда выполнения .Net) и все объекты, созданные на основании этого класса, будут удалены из памяти компьютера сборщиком мусора, когда на них не останется ссылок из других объектов программы. Это значит, что программист может не заботится об очистке памяти компьютера от не нужных объектов - всё сделает сборщик мусора.

Однако, в .Net многие объекты для своей работы используют разнообразные НЕ управляемые ресурсы операционной системы, например, файлы, сетевые подключения, дескрипторы картинок и т.д. Разработчик такого объекта должен позаботится о том, чтобы как можно скорее освободить занятые ресурсы, если объект больше не нужен программе. Казалось бы, для этой цели идеально подходит метод объекта Деструктор, который можно определить в любом классе. Однако, Деструктор объекта будет вызван не сразу, когда на объект больше не осталось ссылок, а через некоторое время, когда сборщик мусора решит заняться очисткой памяти от не нужных более объектов. Более того, согласно документации, в некоторых случаях Деструктор может быть не вызван вовсе, например, при аварийном завершении программы.

Что же делать разработчику, чтобы гарантировано освободить ресурсы? Лучше всего, как только объект стал не нужен, вручную вызвать метод объекта освобождающий занятые им ресурсы. Именно так и рассуждали разработчики .Net.

В .Net предусмотрен специальный интерфейс IDisposable, у которого есть метод Dispose. Разработчик объекта должен реализовать этот интерфейс и описать в методе Dispose алгоритм освобождения ресурсов, а программист использующий объект должен вызвать метод Dispose, как только объект больше не нужен программе. В языке Перфолента для этих же целей может быть использован метод Завершитель.

Вернемся к Перфоленте

В языке Перфолента разработчик класса, должен определить метод Завершитель, для освобождения управляемых ресурсов, и метод Деструктор для освобождения не управляемых ресурсов. Компилятор языка Перфолента автоматически включит необходимую реализацию интерфейса IDisposable и методы Dispose и Finalize, если у класса определен хотя бы один из методов Завершитель или Деструктор. Подробнее об этих методах можно почитать в статье «Конструируем класс. Завершители и Деструкторы».

Гарантированное завершение

Если у объекта есть метод Завершитель или вручную реализован интерфейс IDisposable, то программист может в любой момент вызвать процедуру освобождения ресурсов, занятых этим объектом с помощью оператора ВызватьЗавершитель, и на первый взгляд кажется, что этого достаточно. Однако, уверен ли программист, что программа всегда дойдет до того места, где этот вызов определен? А если случится исключение? А если программист со временем забудет, что в этом коде используются ресурсы, требующие обязательного освобождения, или код будет править другой программист и в коде появится не предусмотренный условный выход из процедуры? Ресурс не будет освобожден?

Да, такое возможно. А как же этого избежать? Способы есть. Можно, например, применить оператор Попытка и в секции Завершение освободить ресурсы с помощью оператора ВызватьЗавершитель. Это сработает!

Однако, есть и более удобный оператор для этой цели…

Оператор «Использовать»

В языке Перфолента.Net существует специальный блочный оператор Использовать, который гарантирует автоматический вызов Завершителя объекта, вне зависимости от того, каким способом осуществлен выход из алгоритма. Этот оператор является синтаксическим сахаром, объединяя синтаксис создания объектов, ресурсы которых необходимо освобождать и автоматическое гарантированное их освобождение.

Рассмотрим синтаксис оператора Использовать:

Использовать Ф1 тип ЗаписьТекста = Новый ЗаписьТекста("log1.txt")
           Ф1.Записать("Запись в файл 1")
КонецИспользовать

Переменная Ф1 определяется прямо в операторе Использовать по аналогии с операторами Для и Для Каждого. Этой переменной присваивается объект, ресурсы которого будут освобождены при выходе из блока любым способом.

Тип переменной Ф1 должен допускать завершение, т.е. иметь метод Завершитель или реализацию интерфейса IDisposable.

На заметку: Как и при определении переменных в операторе Перем, тип переменной в операторе Использовать можно не задавать, если присутствует выражение создающее объект заданного типа.

Допускается использование ранее определенных переменных, однако, в этом случае прежде, чем обращаться к объекту, необходимо убедиться, что он существует:

Перем Ф1 тип ЗаписьТекста
// … … … тут какой-то код
Использовать Ф1
           //объект Ф1 надо инициализировать, если он не определен
           Ф1 = ЕслиНеопределено(Ф1, Новый ЗаписьТекста("log1.txt"))
           // или так (операция ?? аналогична функции ЕслиНеопределено)
           Ф1 = Ф1 ?? Новый ЗаписьТекста("log1.txt")
           //или ещё короче с помощью комбинированного оператора ??=
           Ф1 ??= Новый ЗаписьТекста("log1.txt")
           Ф1.Записать("Запись в файл 1")
КонецИспользовать

В операторе Использовать возможно определение и создание сразу нескольких объектов, каждый из которых будет освобожден при выходе из блока Использовать:

Использовать Ф1 = Новый ЗаписьТекста("log1.txt"),
                          Ф2 = Новый ЗаписьТекста("log2.txt"),
                          Ф3 = Новый ЗаписьТекста("log3.txt")
           Ф1.Записать("Запись в файл 1")
           Ф2.Записать("Запись в файл 2")
           Ф3.Записать("Запись в файл 3")
КонецИспользовать

Секция Исключение

Обычно, алгоритм находящийся внутри блока Использовать можно рассматривать как единое целое. Это значит, что при возникновении ошибки продолжать его выполнение невозможно. Если в случае возникновения ошибки необходимо выполнить какие-либо действия, направленные на исправление ошибочной ситуации, можно применить не обязательную секцию Исключение.

Использовать Ф1 = Новый ЗаписьТекста("log1.txt")
           Ф1.Записать("Запись в файл 1")
Исключение Ош
           ВыводСтроки "Ошибка: "+Ош.ОписаниеОшибки()
КонецИспользовать

На заметку: ВАЖНО! Оператор Попытка ловит ошибку всегда, а оператор Использовать ловит ошибку только тогда, когда задана секция Исключение!!!

Секция Завершение

Оператор Использовать автоматически завершает объекты при выходе из блока. Однако, иногда необходимо выполнить дополнительные завершающие действия, например, послать в линию особый сигнал завершения, прежде, чем линия будет закрыта. В этом случае можно использовать не обязательную секцию Завершение:

Использовать Линия = Новый ЛинияПередачиДанных
           Линия.Записать("Обычные данные")
Завершение
           Линия.ПодписатьДанные("Моя подпись")
КонецИспользовать

Используем все секции

В том случае, когда используются все секции, оператор Использовать выглядит так:

Использовать МойОбъект тип ТипМоегоОбъекта = ВыражениеИнициализацииОбъекта
           //тут работаем с объектом, на который ссылается переменная МойОбъект
Исключение Ош
           //тут обрабатываем ошибку, возникшую при работе с объектом
Завершение
           //тут, выполняем дополнительные завершающие действия с объектом
           // перед его завершением
КонецИспользовать

Оператор «Исп». Сокращенная форма оператора «Использовать»

В некоторых случаях блочный оператор Использовать сильно увеличивает вложенность блоков, что приводит к ухудшению читабельности программы. В таких случаях можно применять его сокращенную форму Исп.

Так же, как и оператор Использовать, оператор Исп гарантирует автоматический вызов завершителя объекта, вне зависимости от того, каким способом осуществлен выход из алгоритма.

Оператор Исп по-прежнему остаётся блочным оператором, однако конец блока Исп компилятор определяет автоматически. Конец блока оператора Исп находится либо в конце того блока, в который он вложен, либо в конце метода, если оператор Исп не вложен в другие блоки.

У оператора Исп нет секций Исключение и Завершение, а значит он не перехватывает ошибки и не позволяет выполнить дополнительные завершающие действия.

Рассмотрим 2 случая, когда оператор Исп находится в блоке Если и когда он не вложен в какой-либо блок.

Случай 1

Процедура Старт
           Если НадоПередатьДанные
                       // оператор ИСП действует до конца окружающего его блока
                       // в данном случае до конца блока Если
                       Исп Линия = Новый ЛинияПередачиДанных
                       Линия.ПередатьДанные()
                       //….  остальной код метода
                       //….
                       // вот тут, в конце блока ЕСЛИ будет освобождена переменная Линия
                       // тут закончится блок оператора ИСП
           КонецЕсли
КонецПроцедуры

Случай 2

Процедура Старт
           // оператор ИСП действует до конца метода
           // т.к. он не вложен ни в один блок
           Исп Линия = Новый ЛинияПередачиДанных
           Если НадоПередатьДанные
                       //вызов методов объекта может быть вложенным в другие блоки
                      Линия.ПередатьДанные()
           КонецЕсли
           //….  остальной код метода
           //….
           // вот тут, в конце метода будет освобождена переменная Линия
           // тут закончится блок оператора ИСП
КонецПроцедуры

На заметку: ВАЖНО! Переменная, объявленная в операторе Исп действительна до конца метода, однако, в конце блока Исп для неё будет вызван Завершитель и она получит значение Неопределено!!! Это не помешает Вам инициализировать и использовать её снова ниже по коду метода.

Вывод: Оператор Использовать и его сокращенная форма Исп позволяют автоматически гарантированно освобождать ресурсы, занятые заданным объектом. Оператор Использовать при необходимости позволяет перехватывать возникающие при работе с объектом ошибки и/или выполнять дополнительные завершающие действия перед освобождением ресурсов и окончательным завершением работы объекта.

Сергей Рогаткин 27.06.2021 13:12

См. также

Ссылки