Макроподстановка
Формирование параметра команды в случае, если синтаксис FoxPro не предполагает его наличия
Использование условных операторов
Ветвление кода
Выражение имени. Скобки
Формирование имени объекта
Функция GetPem()
Коллекции объектов
Метод SetAll()
Формирование значения или выражения для его вычисления
Формирование символьных строк
Сложение и вычитание символьных строк
Функция TextMerge()
Текстовые скобки Text…EndText
Функция Evaluate()
Формирование команды
Использование макроподстановки
Особенности использования макроподстановки
Восстановление исходных настроек среды
Настройка SET FILTER
Формирование команды Select-SQL
Другие команды и функции, связанные с макроподстановкой
Макроподстановка нескольких строк кода.
Функция ExecScript()
Методы системного объекта _VFP
Метод _VFP.Eval()
Метод _VFP.DoCmd()
Метод _VFP.SetVar()
Макроподстановка – это одна из тех вещей, которые достаточно просто понять на практике, но довольно сложно дать определение «словами».
Типичные примеры использования макроподстановки выглядят примерно так
* Макроподстановка в SQL-запросах Local lcFieldList, lcWhere lcFieldList = 'Field1, Field2, Field3' lcWhere = 'FieldId = 1' select &lcFieldList from MyTab where &lcWhere * Макроподстановка в выражениях Local lcCommand lcCommand = 'lnVar = 1+2' &lcCommand ?' lnVar = ', lnVar
* Макроподстановка в SQL-запросах Local lcFieldList, lcWhere lcFieldList = 'Field1, Field2, Field3' lcWhere = 'FieldId = 1' select &lcFieldList from MyTab where &lcWhere * Макроподстановка в выражениях Local lcCommand lcCommand = 'lnVar = 1+2' &lcCommand ?' lnVar = ', lnVar
С точки зрения FoxPro в процессе выполнения данных примеров код примет вид
* Макроподстановка в SQL-запросах select Field1, Field2, Field3 from MyTab where FieldId = 1 * Макроподстановка в выражениях lnVar = 1+2 ?' lnVar = ', lnVar
Т.е. в процессе выполнения произойдет замена ссылки (переменной) на собственно значение. Понятно, что значение переменной может формироваться динамически, в зависимости от ряда условий или диалога с пользователем. Причем формироваться не на этапе компиляции программы, а непосредственно в процессе исполнения. Тогда определение макроподстановки можно выразить примерно такими словами
Определение
Макроподстановка – это средство исполнения кода, окончательный синтаксис которого формируется в процессе его исполнения.
Другими словами, на этапе написания кода заранее неизвестно, какой именно код будет исполняться. Это уточняется в процессе исполнения. За это свойство макроподстановки его очень любят новички, поскольку они всегда склонны писать некие «универсальные» (на все случаи жизни) алгоритмы. Однако опытные программисты относятся к макроподстановкам более сдержано. Как правило, по следующему принципу
Применение
Если существует альтернатива использованию макроподстановки без серьезного ущерба коду (по скорости, «читабельности», объему и т.п.), то лучше использовать эту альтернативу. По возможности, макроподстановку лучше не использовать.
Почему, собственно? Пожалуй, здесь подойдет такая аналогия. Представьте себе, что Вы решили купить какую-нибудь «мелочевку», ну, там, салфетки или карандаш. В общем, то, что стоит «копейки». Будете ли Вы расплачиваться крупной купюрой в несколько тысяч? Ну, можно, конечно, только Вы рискуете получить на сдачу мешок железной мелочи, который еще непонятно куда девать. А если все-таки решите пересчитать всю эту мелочь, может оказаться, что Вас обсчитали. Не обязательно по злому умыслу, просто попробуйте точно пересчитать такую гору мелочи.
Т.е. макроподстановка, зачастую, это «слишком много». Инструмент, не адекватный поставленной задаче. Нет, она, конечно, задачу решает, но затрачивает на ее решение избыточное количество ресурсов, при этом повышая риск возникновения ошибки. Опытный программист, по возможности, старается платить за «покупку» ровно столько, сколько она стоит, чтобы не ждать сдачи и не заниматься ее пересчетом. Т.е. использовать инструмент, который специально создан для решения конкретной задачи.
Данная статья как раз и посвящена тому, какие есть альтернативы макроподстановке, а также что и в каких случаях лучше использовать.
Для новичков проблемой является тот факт, что макроподстановка – это универсальный инструмент, ну, как «газовый (разводной) ключ». А ее альтернативы – это довольно большой набор «комплекта ключей». Т.е. альтернативны макроподстановке – это узкоспециализированные инструменты, приспособленные для решения довольно ограниченного круга задач. Новичку проще таскать с собой (изучить) один инструмент, чем несколько.
Следует отдавать себе отчет в том, что хотя, как правило, при помощи макроподстановки делается вставка некоего фрагмента команды, но, с точки зрения FoxPro, формируется вся команда целиком. По вполне понятной причине. Заранее просто невозможно сказать, какие именно опции (части) формируемой команды включены в макроподстановку. С другой стороны, альтернативы макроподстановки как раз интерпретируются FoxPro именно как части команд. Некие «параметры».
Чтобы как-то структурировать все дальнейшие объяснения, стоит выделить наиболее типичные случаи использования макроподстановки
Как правило, макроподстановки используют в следующих случаях
- Формирование параметра команды в случае, если синтаксис FoxPro не предполагает его наличия
- Формирование имени объекта
- Формирование значения или выражения для его вычисления
- Формирование команды
Вот и посмотрим, какие есть альтернативы при решении данных задач
Формирование параметра команды в случае, если синтаксис FoxPro не предполагает его наличия
В синтаксисе FoxPro многие команды не предполагают наличие параметра, хотя это часто необходимо. Типичные случаи - это все команды SET, все команды работы с файлами (USE, MODIFY, COPY, APPEND и т.п.) и ряд других случаев. В зависимости от того, о какой команде и каком «параметре» идет речь, существуют различные альтернативы макроподстановке.
Использование условных операторов. Ветвление кода
Довольно часто в программном коде возникает необходимость установки или восстановления некоторых глобальных настроек в заранее заданное значение. Однако большинство команд настроек не предусматривают указания параметра. Там просто ставится конкретное значение. Например, ON или OFF. Как же поступить, если надо задать конкретное, заранее известное значение? Команды SET в подавляющем большинстве случаев имеют всего два возможных значения. Как правило, это значения ON или OFF. Поэтому вместо макроподстановки можно использовать оператор IF. Примерно так
lcNeedValue = 'OFF' && Значение, которое должна иметь настройка * Сравниваем текущее значение настройки с тем, которое необходимо IF SET('DELETED') <> lcNeedValue SET DELETED OFF ENDIF
Как видите, все просто. Если текущее значение настройки отлично от того, которое нужно, то просто делаете эту настройку без макроподстановки.
Выражение имени. Скобки
Во многих случаях требуется сформировать имя чего-либо в качестве «параметра», когда команда FoxPro не предусматривает наличия параметра. Типичные случаи подобных ситуаций – это команды USE, COPY TO, REPLACE. Для данных ситуаций существует, так называемое, «выражение имени». Это обычные круглые скобки. Их использование выглядит следующим образом
* Имя файла lcFileName = getFile() USE (m.lcFileName) IN 0 lcFileSave = putFile() COPY TO (m.lcFileSave) * Имя поля lcFieldName = 'MyField' select MyTable REPLACE (m.lcFieldName) WITH 2 * Имя переменной lcVariableName = 'MyVariable' * Команда не может начинаться с круглой скобки. Будет сообщение о синтаксической ошибке * (m. lcVariableName) = 1 * Однако можно использовать команду STORE STORE 1 TO (m. lcVariableName)
Выражение имени (круглые скобки) можно рассматривать как некий усеченный вариант макроподстановки, но такой, который в результате своей работы формирует имя чего-либо. Отсюда и название «выражение имени». Т.е. выражение, формирующее имя. Чем выражение имени лучше «обычной» макроподстановки? Основное преимущество – это более высокая определенность кода. Во-первых, очевидно, что за выражением имени «скрывается» одно значение. Один «параметр» команды. При использовании макроподстановки – это далеко не очевидно. Ведь макроподстановка может включать в себя еще набор каких-либо дополнительных опций команды. Для выражения имени это просто исключено. Во-вторых, очевидно, что за выражением имени «скрывается» именно имя чего-либо, а не какая-либо опция команды. Что, опять же, не очевидно для макроподстановки. Есть еще некоторые дополнительные преимущества использования выражения имени. Но это уже из разряда приятных дополнений.
- Нечувствительность к пробелам в имени
- Несколько более высокая скорость работы
Подробнее про пробелы в именах можно почитать здесь Как работать с путями доступа и именами файлов, содержащих пробелы Однако следует понимать, что выражение имени можно использовать только в том случае, если формируемый «параметр» состоит только и исключительно из имени. Например,
LOCATE FOR MyField = 1
Здесь «параметром» является выражение «MyField=1», но никак не имя. Поэтому использовать здесь выражение имени просто невозможно. У данной команды нет «параметра» «имя», а применить выражение имени, к части «параметра» (только для имени поля) – невозможно.
Формирование имени объекта
Зачастую, возникает необходимость сформировать имя объекта, но не для какой-либо команды, а для получения доступа к свойствам и методам нужного объекта. Другими словами, получить ссылку на объект, имя которого будет сформировано динамически. Как правило, речь идет о каких-либо объектах на форме. Например, об определенном столбце Grid или определенном объекте TextBox.
Функция GetPem()
Данная функция идеальный вариант замены макроподстановки в случае, если Вам надо получить ссылку на какой-либо конкретный (одиночный) объект по его имени.
* Ссылка на объект TextBox лежащий на форме lcObjectName = 'TextBox' + '1' loObject = GetPem(ThisForm, m.lcObjectName) ?loObject.ReadOnly * Ссылка на объект Column, лежащий в Grid1 lcObjectName = 'Column' + '1' loObject = GetPem(ThisForm.Grid1, m.lcObjectName) ?loObject.ReadOnly
Однако в случае перебора многих объектов это не очень-то удобно. Но для этого случая есть другие варианты
Коллекции объектов
Дело в том, что каждый объект-контейнер FoxPro содержит в себе две специальные коллекции вложенных объектов. В обязательном порядке будет существовать коллекция с именем «Objects» и еще одна коллекция, имя которой будет зависеть от типа объекта-контейнера:
- Controls (Form, Control, Container, Page, Column)
- Columns (Grid)
- Pages (PageFrame),
- Buttons (CommandGroup или OptionGroup)Как следствие, можно организовать перебор объектов в коллекции. Либо просто по номерам, используя цикл FOR…ENDFOR, либо циклом FOR EACH. Например, перебрать все объекты Column в Grid можно следующими способами
* Перебор по физическому номеру столбца в коллекции FOR m.lnI = 1 TO ThisForm.Grid1.ColumnCount loColumn = ThisForm.Grid1.Columns[m.lnI] ?m.lnI, ' Name= ', loColumn.Name ENDFOR * Перебор всех объектов определенного типа FOR EACH loColumn IN ThisForm.Grid1.Columns ?'Name= ', loColumn.Name ENDFOR
Замечание Следует иметь в виду, что порядковый номер столбца в коллекции столбцов Grid, в общем случае, не имеет никакого отношения к имени столбца. Т.е. тот факт, что по умолчанию столбец с номером 1 имеет имя 'Column1', вовсе не означает, что после определенных модификаций с Grid тот же самый столбец сохранит тот же самый порядковый номер. Аналогичное замечание относится и ко всем другим однотипным элементам специализированных контейнеров. Page в контейнере PageFrame или Buttons в CommandGroup.Метод SetAll()
Довольно часто совсем не нужно знать конкретное имя объекта. Нужно просто установить значение определенного свойства для всех объектов нужного типа в объекте-контейнере. Ну, например, установить для всех объектов TextBox на форме значение ReadOnly=.F. можно следующим способом
ThisForm.SetAll('ReadOnly', .F., 'TextBox')
Установить для всех объектов Column в Grid свойство DynamicBackColor таким образом, чтобы цвет фона записей чередовался с белого на зеленый
ThisForm.Grid1.SetAll("DynamicBackColor", "IIF(MOD(RECNO( ), 2)=0, RGB(255,255,255), RGB(0,255,0))", "Column")
Метод SetAll позволяет просто отказаться от ручного перебора объектов и, как следствие, от необходимости указания имен конкретных объектов. Хотя, конечно, если надо установить значение свойства по принципу «все, кроме», то без перебора все-равно не обойтись. Впрочем, если эти самые «кроме» созданы на базе другого класса, то можно и без перебора. Но это уже детали реализации.
Формирование значения или выражения для его вычисления
Наиболее типичный пример данной задачи – это формирования символьной строки с неким сообщением. Как правило, текст этого сообщения формируется из фрагментов, которые могут находиться в разных полях и переменных. Впрочем, бывает необходимо формировать не только текстовые строки.
Формирование символьных строк
С этой задачей сталкивались практически все. Существует довольно большое количество вариантов ее решения. Строго говоря, как правило, никому в голову не приходит формировать текстовую строку через макроподстановку, просто потому, что есть очевидные и более простые способы. Однако макроподстановка просто невозможна без предварительного формирования символьной строки. Именно поэтому способам формирования символьных строк в данной статье посвящено относительно много места. Как некоему предварительному этапу макроподстановки или ее аналогов.
Сложение и вычитание символьных строк
Символьные строки в FoxPro можно складывать и вычитать.
lcString = 'Начало строки ' + ' окончание строки'
Результат этой операции будет простое «сложение» или конкатенация символьных строк. Т.е. к концу первой строки будет добавлена вторая строка «как есть». В результате получим
lcString = 'Начало строки окончание строки'
Символьные строки можно еще и «вычитать»
lcString = 'Начало строки ' - ' окончание строки'
Результат этой операции будет «сложение» или конкатенация символьных строк, однако все концевые пробелы каждого «слагаемого» будут перенесены в конец итоговой строки. Т.е. в результате получим
lcString = 'Начало строки окончание строки '
Операцией вычитания редко пользуются, поскольку большинство складываемых строк обычно не имеют ведущих пробелов. Как следствие, при выполнении операции вычитания символьных строк начало второй части просто сольется с окончанием первой. Между ними не окажется пробела. Естественно, в качестве слагаемого можно использовать не только символьные константы, но и поля таблиц. Единственное ограничение заключается в том, что значение поля должно быть преобразовано в символьный тип данных. В большинстве случаев с этим с успехом справляется функция TransForm()
lcString = 'Значение поля Field1 = ' + TransForm(MyTable.Field1)
Однако есть много других функций для преобразования в символьный тип данных: STR(), DTOC(), TTOC(), CAST()
Функция TextMerge()
Явное преобразование содержимого поля в символьный тип данных, конечно, хорошо, но удобнее пользоваться для этой цели функцией TextMerge()
lcString = TextMerge('Значение поля Field1 = <<MyTable.Field1>>')
Т.е. здесь прямо внутри символьной строки в окружении специальных символов пишется имя поля и функция TextMegre() автоматически преобразовывает его содержимое в символьный тип данных, вставляя этот текст вместо имени поля
Текстовые скобки Text…EndText
В случае если необходимо написать большой текст, занимающий несколько строк, то удобнее пользоваться текстовыми скобками Text…EndText. Все, что находится внутри них, воспринимается как обычный текст, а не программный код
TEXT TO lcText TEXTMERGE NOSHOW Значение поля Field1 = <<MyTable.Field1>> Значение поля Field2 = <<MyTable.Field2>> Значение поля Field3 = <<MyTable.Field3>> ENDTEXT * Смотрим, что получилось ?lcText
Как видите, нет необходимости в дополнительных кавычках и как-либо формировать перенос строк. Вы пишите текст так, как будто вы его пишите в текстовом редакторе. Ну, а специальные «скобки» в сочетании с опцией TEXTMERGE позволяют вставлять в текст содержимое полей таблицы.
Функция Evaluate()
Иногда надо не просто сформировать текстовую строку, но вычислить некое значение любого типа. В этом случае лучше использовать функцию Evaluate()
select MyTab lcExpression = 'Field1+2' ?Evaluate(m.lcExpression) lcFieldName = 'Field1' replace (lcFieldName) with Evaluate(m.lcExpression)
Формирование команды
В большинстве случаев, это как раз та ситуация, когда нет альтернативы использования макроподстановки. В простейшем случае, это выглядит так.
lcComand = 'lnVar = 1 + 2' &lcComand ?'Значение переменной lnVar=', lnVar
Однако в данном случае следует серьезно подумать о необходимости использования подобной макроподстановки. Слишком уж «не прозрачным» становится код. Совершенно не понятно, что скрывается за макроподстановкой. Может быть, имеет смысл разбить макроподстановку на две части?
lcVarName = 'lnVar' lcExpression = '1+2' STORE Evaluate(lcExpression) TO (lcVarName) ?'Значение переменной lnVar=', lnVar
На практике, разумеется, в качестве команды выступают боле сложные команды. Обычно это команды Select-SQL. Но об этом чуть ниже.
Использование макроподстановки
Несмотря на все приведенные ранее примеры, тем не менее, есть ситуации, когда без макроподстановки просто не обойтись. Не то, чтобы это было в принципе невозможно, просто решение без макроподстановки будет более громоздким. Ниже приведены наиболее типичные случаи, когда макроподстановка оправдана. Разумеется, приведенными примерами далеко не исчерпывается список случаев ее использования. Просто все прочие варианты использования относительно редки.
Особенности использования макроподстановки
Прежде чем описывать случаи использования макроподстановки следует остановиться на некоторых особенностях самой команды макроподстановки. Основной особенностью использования макроподстановки является тот факт, что первая точка после символа макроподстановки интерпретируется не как разделитель объектов, а как символ «сложения» того текста, который стоит «внутри» макроподстановки и текста, следующего за символом точки.
lcName = 'TextBox1' * Следующая команда вызовет довольно странное сообщение об ошибке ?ThisForm.&lcName.ReadOnly
Текст ошибки будет звучать примерно так: Объекта «TextBox1ReadOnly» не существует. Как видите, символ первой точки просто «прибавил» к содержимому макроподстановки все то, что следовало за ним. Как же определяется окончание того фрагмента, который должен быть «прибавлен» к содержимому макроподстановки? «Сложение» будет идти до тех пор, пока не встретится еще один символ точки или любой другой символ, означающий окончание опции (фрагмента) команды (пробел, равенство, запятая и т.п.). Как следствие, если необходимо избежать подобного «сложения», то просто ставят две точки подряд
* Следующая команда имеет 2 точки подряд после символа макроподстановки ?ThisForm.&lcName..ReadOnly
При таком синтаксисе, к содержимому макроподстановки «прибавляется» пустая строка, поскольку после первой точки и до второй – ничего нет. А вторая точка уже служит обычным разделителем имен объектов.
Восстановление исходных настроек среды
Правилом хорошего тона считается восстановление исходных настроек среды, после того, как с ней закончили работать. Разумеется, дело тут не просто в хороших манерах, а в том, чтобы вернувшись, например, в родительскую форму после работы в дочерней форме не получить ряд проблем из-за изменившихся в дочерней форме настроек среды окружения. Как уже было показано ранее, очень многие настройки среды могут принимать только одно из двух значений и их можно восстановить без макроподстановки простым ветвлением кода. Однако далеко не все. Например, настройку ON ERROR
lcOldError = ON('Error') ON ERROR DO MyProg * Дальнейшая работа со средой * Восстановление исходных настроек после завершения работы ON ERROR &lcOldError
Здесь просто нет вариантов, поскольку «параметром» для настройки ON ERROR выступает никак не «имя» и не «выражение». В данном случае «параметром» является команда. А для команды нет альтернативы. Аналогичное правило действует и в отношении любых других настроек, если «параметр» настройки нельзя свести к ограниченному набору значений, имени чего-либо или выражению. Нет, разумеется, и здесь есть варианты решения без макроподстановки, но довольно громоздкие. Например, как можно было бы решить вопрос с ON ERROR? Да очень просто. Сделать процедуру, которая бы по содержимому переменной lcOldError выполняла бы соответствующую настройку. Ну, примерно так
DO CASE CASE lcOldError = 'DO MyProg' ON ERROR DO MyProg CASE lcOldError = … ENDCASE
Но, Вы представляете себе эту «простыню»? Т.е. сколько строк кода должно быть в подобной процедуре? Это не говоря о том, что процедура ON ERROR может быть контекстно-зависимой, т.е. привязанной к какому-либо объекту, ссылку на который тоже придется передавать в эту процедуру. В общем, мороки много, а толку мало. Макроподстановка в данном случае предпочтительнее во всех смыслах.
Настройка SET FILTER
Вообще-то, настройка SET FILTER не обладает достаточной гибкостью. Как следствие, годится только для довольно простых случаев фильтрации, когда надо «по-быстрому» решить очередную «хотелку» пользователя. Если данная задача впоследствии «обрастет» дополнительными требованиями, то, скорее всего от SET FILTER придется отказаться в пользу запросов. Просто SET FILTER не справится с возросшими требованиями. Тем не менее, в простых случаях SET FILTER может оказаться полезным. Как правило, фильтрация осуществляется на основании некоего значения, введенного в объект на форме. Поэтому условие фильтра обычно выглядит так
Select MyTab SET FILTER TO MyField = ThisForm.TextBox1.Value
И это работает. Более того, при изменении значения TextBox1 даже не надо ничего делать, фильтр изменится автоматически. Однако, тем не менее, подобные конструкции лучше не использовать. В чем здесь подвох? Дело в том, что настройка SET FILTER привязана к рабочей области, а вовсе не к форме. Это значит, что если Вы перейдете в другую форму и обратитесь к той же самой рабочей области, то получите сообщение об ошибке вроде «Объект TextBox1 не существует». Действительно не существует. Ведь Вы же перешли в другую форму. В ту, где подобного объекта нет. Но фильтр-то об этом не знает. Он упорно пытается обратиться по тому «адресу», который ему и указали. Разумеется, это можно обойти, создав глобальную переменную со ссылкой на форму. Но это тот случай, когда лекарство хуже болезни. После этого придется предпринимать ряд усилий по контролю над этой переменной и ее своевременным уничтожением. Значительно проще проблема решается при использовании макроподстановки следующим образом
* Если значение числового типа lnValue = ThisForm.TextBox1.Value SET FILTER TO MyField =&lnValue * Если значение символьного типа lcValue = ['] + ThisForm.TextBox1.Value + ['] SET FILTER TO MyField =&lcValue
Другими словами, при помощи макроподстановки заменяем ссылку непосредственно значением, содержащимся в этой переменной. Как следствие, настройка SET FILTER оказывается не зависимой от контекста. Она больше не привязана к объектам формы. Правда, небольшим недостатком данной замены является необходимость повторного назначения фильтра при изменении значения в объекте на форме. Но это даже недостатком можно назвать условно. Смотря, с какой точки зрения на это смотреть.
Формирование команды Select-SQL
Это один из наиболее часто встречающихся случаев использования макроподстановки. Выглядит примерно так
Local lcFieldList, lcWhere lcFieldList = 'Field1, Field2, Field3' lcWhere = 'FieldId = 1 and Field1 in (1,2,3)' select &lcFieldList from MyTab where &lcWhere
Здесь также, по сути, нет альтернативы использованию макроподстановки. Список полей, это, конечно же, список имен, однако выражение имени может быть использовано только для одного имени, а не для списка. Запрос, возвращающий одно поле – достаточно редко встречается. Да и при использовании выражения имени придется дополнительно указывать имя поля в результирующей выборке, иначе оно получит имя вроде «Expr_1», ведь было вычислено выражение. В условии отбора использовать Evaluate() тоже не получится. Почему? Ну, кратко это описать затруднительно. Тут несколько причин. В зависимости от того, какое именно выражение необходимо вычислить. Если Вы попытаетесь использовать Evaluate(), то, скорее всего, ошибки это не вызовет, но вот результат запроса окажется далек от ожидаемого. Если и совпадет, то разве что случайно. Здесь еще раз уместно упомянуть тот факт, что даже один символ макроподстановки с точки зрения FoxPro означает формирование команды целиком. Это значит, что будут задействованы все механизмы оптимизации SQL-запросов. Макроподстановка не отключает оптимизацию. Соответственно, можно формировать не части команды, а всю команду сразу
Local lcSelectSQL lcSelectSQL = 'select Field1, Field2, Field3 from MyTab where FieldId = 1 and Field1 in (1,2,3)' &lcSelectSQL
Подобный способ имеет как преимущества, так и недостатки. Преимуществом является тот факт, что в режиме отладки можно посмотреть на сформированную команду (содержимое символьной переменной lcSelectSQL) и понять в чем ошибка (если есть), просто запустив эту команду в командном окне. Недостатком же является отсутствие «наглядности». Совершенно непонятно, как именно сформирована строка макроподстановки. Из каких «слагаемых» она собрана.
Другие команды и функции, связанные с макроподстановкой
Кроме описанных ранее команд и функций следует упомянуть еще ряд команд и функций. Подробно их описывать нет смысла, поскольку документация по ним достаточно полная.
Макроподстановка нескольких строк кода. Функция ExecScript()
Одним из ограничений макроподстановки является тот факт, что при помощи нее можно выполнить только одну команду. Если Вам необходимо выполнить несколько команд, то Вы вынуждены будете последовательно выполнять по одной команде. Но как быть, например, с операторами цикла или условия? Ведь они состоят из двух команд? Ну, строго говоря, подобных макроподстановок лучше избегать. Это еще более сложная и не однозначная задача, чем «простая» макроподстановка. Тем не менее, в этих случаях поступают следующим образом. Формируется символьная строка, по сути, содержащая фрагмент файла PRG. Затем этот файл компилируется и выполняется. Сделать это можно буквально. Сохранив символьную строку как файл PRG при помощи функции StrToFile(), откомпилировав его командой Compile, и запустив на исполнение командой Do. После этого надо не забыть удалить временный файл с диска и выгрузить его из памяти. Однако то же самое можно проделать не явно, специальной функцией ExecScript(). Фактически, эта функция берет на себя работу этих трех команд. Формирует файл, компилирует его и выполняет. Также эта функция и убирает за собой. Поскольку, по сути, функция ExecScript() выполняет отдельную процедуру, то следует помнить об области видимости переменных, если Вы собираетесь использовать их внутри данной функции. Еще раз напомню, что для FoxPro данная функция еще более «тяжелая», чем обычная макроподстановка. Поэтому, если речь идет об одиночной команде, то, лучше все-таки использовать обычную макроподстановку.
Методы системного объекта _VFP
Существуют ситуации, когда необходимо выполнить макроподстановку, но не как команду, а как функцию. Наиболее часто в этом возникает необходимость внутри отчетов, где использование команд просто невозможно. Разумеется, это можно сделать, создав специальную функцию, в которую в виде параметра передавать строку. А внутри функции уже выполнить команду макроподстановки для данной строки. Но эту задачу можно решить при помощи специальных методов системного объекта _VFP
Метод _VFP.Eval()
Данный метод – это полный аналог функции Evaluate(). Т.е. результат их работы будет полностью идентичен.
lcExpression = '1+2*3' ?_VFP.Eval(m.lcExpression) * Тот же самый результат даст следующая команда ?Evaluate(m.lcExpression)
Метод _VFP.DoCmd()
Данный метод – это полный аналог макроподстановки для целой команды. Т.е. результат их работы будет полностью идентичен.
lcCommand = 'Wait window [Подождите…] timeout 10' _VFP.DoCmd(m.lcCommand) * Тот же самый результат даст следующая команда &lcCommand
Метод _VFP.SetVar()
Данный метод это аналог присвоения значения переменной через макроподстановку. Причем макроподстановка используется для имени переменной.
lcVarName = 'lnMyVar' _VFP.SetVar(m.lcVarName, 123) ?lnMyVar * Тот же самый результат даст следующая команда Store 123 TO (m.lcVarName) ?lnMyVar