При разработке на платформе 1С:Предприятие 8.3 многие программисты сталкиваются с интересной ситуацией: если добавить реквизит формы интерактивно (в конфигураторе), функция Вычислить() без проблем с ним работает. Но стоит удалить его и создать программно, как платформа начинает выдавать ошибку компиляции: Переменная не определена. Давайте подробно разберем эту ситуацию, проанализируем причины такого поведения платформы и найдем наиболее правильные варианты решения.
Рассмотрим классический пример. Допустим, на форме был заведен реквизит S. Расчет функции в коде проходил нормально:
Результат = Вычислить("S*1.5");
Затем мы решаем оптимизировать форму и создаем этот реквизит программно через метод ИзменитьРеквизиты(). При попытке выполнить тот же самый код платформа выдает ошибку:
{Модуль(1,2)}: Переменная не определена (S)
При этом, если изменить вызов на Вычислить("ЭтаФорма.S*1.5"), расчет выполняется успешно. Возникает закономерный вопрос: это баг платформы или задокументированная особенность (фича)?
Чтобы понять причину, проанализируем, как встроенный язык 1С обрабатывает переменные и реквизиты. Платформа жестко разделяет два этапа: этап компиляции (когда модуль формы только инициализируется и проверяется на синтаксис) и этап исполнения (runtime).
ПриСозданииНаСервере). На этапе компиляции платформа еще не знает о существовании этих реквизитов.Когда вы пишете в коде прямое обращение к программно созданному реквизиту, компилятор воспринимает его как необъявленную локальную переменную. Посмотрим на пример кода, который обязательно вызовет ошибку компиляции:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Мас = Новый Массив;
Мас.Добавить(Новый РеквизитФормы("S", Новый ОписаниеТипов("Строка")));
ИзменитьРеквизиты(Мас);
// Здесь возникнет ОшибкаКомпиляцииВстроенногоЯзыка: Переменная не определена (S)
Рез = "" + S;
КонецПроцедуры
Именно поэтому строка "S*1.5", переданная в функцию Вычислить(), не находит S в базовом контексте модуля и закономерно падает.
Выясним, почему конструкция ЭтаФорма.S отрабатывает без ошибок. ЭтаФорма — это системная переменная, представляющая собой объект самой управляемой формы.
При обращении к свойствам объекта через точку (в нашем случае .S) платформа включает механизм динамического поиска свойств. Этот поиск происходит непосредственно в режиме реального времени (runtime), когда программный реквизит уже был успешно создан и добавлен в структуру формы. Механизм динамического связывания видит новый реквизит и корректно извлекает его значение для вычислений.
Несмотря на то, что конструкция ЭтаФорма.ИмяРеквизита работает, в стандартах разработки 1С обращение к программным реквизитам через точку считается плохим тоном. Это может привести к конфликтам, если имя реквизита совпадет с зарезервированным словом платформы или именем метода.
Рассмотрим наиболее надежный и правильный способ — обращение к форме как к коллекции. Для программно созданных реквизитов всегда следует использовать квадратные скобки:
// Правильное чтение значения программного реквизита
ЗначениеРеквизита = ЭтаФорма["S"];
// Правильное использование в функции Вычислить()
Результат = Вычислить("ЭтаФорма[""S""] * 1.5");
Такой подход защищает код от непредвиденных ошибок, особенно когда имена реквизитов формируются динамически в цикле.
Если мы планируем вычислять выражения с переменными, которые создаются "на лету", проанализируем несколько важных правил работы с динамической структурой формы:
ИзменитьРеквизиты() — это крайне ресурсоемкая серверная процедура, которая перестраивает кэш формы и заново генерирует ее элементы. Если нужно создать много реквизитов, всегда собирайте их в один Массив и вызывайте метод ИзменитьРеквизиты() строго один раз, избегая вызовов внутри циклов.ЭтаФорма["S"] будет без проблем работать и в клиентских процедурах.Вычислить(), убедитесь, что реквизит существует. Для этого можно получить массив текущих реквизитов с помощью метода ПолучитьРеквизиты().Таким образом, описанное поведение — это не баг, а архитектурная особенность платформы 1С:Предприятие (фича), связанная с разделением контекстов компиляции и исполнения. Применяйте обращение через коллекцию ЭтаФорма["Имя"], и ваш код будет стабильным и предсказуемым в любых сценариях.