Реализация конструкции try-finally на VBScript

Материал из GedeminWiki
Перейти к: навигация, поиск

Грамотно написанный программный код должен заботиться о высвобождении выделенных ресурсов по окончании их использования даже в случае аварийного завершения функции или процедуры. В Делфи мы имеем конструкцию try-finally, которая гарантирует, что код в секции finally будет выполнен в любом случае:

 ...
 // выделение ресурса
 try
   ...
   // использование ресурса
   ...
 finally
   // высвобождение ресурса
 end;
 ...

В языке VBScript такая конструкция отсутствует, а обработка ошибок с помощью On Error Resume Next/On Error GoTo 0 весьма трудоемка. Как быть? На помощь приходит свойство экземпляров классов VBScript, которые автоматически высвобождаются по выходу из соответствующей области видимости. Вспомогательный класс TCreator уже долгое время используется для уничтожения объектов по завершении кода функции или процедуры. Его метод GetObject создает объект с помощью вызова функции Designer.CreateObject и сохраняет ссылку на созданный объект во внутреннем списке. По выходу из кода, объект TCreator уничтожается, при этом выполняется его деструктор, который просматривает список ранее созданных объектов и вызывает Designer.DestroyObject для каждого из них. Высвобождение объектов -- частный случай кода завершения функции. На практике, часто необходимо вызвать некоторый метод объекта или выполнить произвольный программный код. Например, после блокировки визуального отображения набора данных с помощью вызова метода DataSet.DisableControls, необходимо обязательно восстановить исходное состояние с помощью DataSet.EnableControls. К счастью, VBScript предоставляет нам возможность выполнять произвольный программный код с помощью процедуры Execute. Таким образом, мы можем сделать следующее:

  • Добавим в TCreator двумерный массив, который будет хранить список команд и ссылку на объект/переменную, соответствующую каждой команде. Причем, в тексте команды имя объекта или переменной мы должны указывать с помощью мнемонического кода <X>.
  • Добавим метод AddFinallyCode(ByRef S, ByRef Obj) с помощью которого в список добавляется команда и соответствующий ей объект или переменная. AddFinallyCode -- это функция, которая возвращает индекс добавленной команды.
  • Для удобства, предусмотрим метод DeleteFinallyCode(I), который удаляет из списка команду, заданную индексом.
  • Изменим деструктор класса TCreator так, чтобы перед уничтожением списка созданных с помощью GetObject объектов, сначала выполнялись команды из нашего внутреннего списка.

В итоге, мы сможем использовать класс TCreator следующим образом:

 Option Explicit
 Sub ProcessData(ByRef DataSet)

   Dim Creator
   Set Creator = New TCreator
   
   DataSet.DisableControls
   Creator.AddFinallyCode "<X>.EnableControls", DataSet

   ' выполняем обработку набора данных
   ' по окончании процедуры состояние датасета восстановится
   ' с помощью команды DataSet.EnableControls

 End Sub

Следует помнить, что если вторым параметром на вход функции TCreator.AddFinallyCode передан объект, то сохраняется ссылка на него. Если же передается переменная, то сохраняется ее значение.

 Option Explicit
 Sub TestFinallyCode
 
   Dim Creator, I, A
   I = 5
   Creator.AddFinallyCode "MsgBox ""I = "" & <X>", I

   I = 1
   A = 1 / 0 ' вызываем ошибку -- деление на ноль

   I = 2
 End Sub

В результате выполнения такой процедуры, на экран будет выведено сообщение:

 I = 5

а не I = 1, как можно было подумать.

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Инструменты