Одной из самых частых трудностей, с которой сталкиваются начинающие разработчики на платформе 1С:Предприятие, является непонимание областей видимости переменных и механизмов взаимодействия между процедурами. Ситуация, когда переменная объявлена в одной процедуре, но "не видна" в другой, является классической.
В этой статье мы подробно разберем, почему возникает эта ошибка, рассмотрим два основных способа решения задачи (передача через параметры и использование переменных модуля), а также проанализируем лучшие практики написания кода, чтобы избежать ошибок в будущем.
Прежде чем переходить к решению, давайте разберем, почему исходный код не работает (в чем мог бы помочь анализатор кода). Рассмотрим типичную ситуацию:
Процедура Первая() Экспорт
а = 1;
b = 2;
Вторая();
КонецПроцедуры
Процедура Вторая() Экспорт
с = b + a; // Ошибка: Переменные не определены
КонецПроцедуры
Когда мы объявляем переменные а и b внутри процедуры Первая, они становятся локальными. Это означает, что они существуют в памяти компьютера только в тот момент, когда выполняется процедура Первая. Как только выполнение переходит в процедуру Вторая, контекст меняется. Процедура Вторая ничего не знает о том, что происходило внутри Первая, и не имеет доступа к её локальным переменным.
Чтобы исправить это, нам нужно явно передать данные. Рассмотрим варианты решения.
Самый правильный и безопасный способ — это использование параметров процедуры. Мы должны "сказать" процедуре Вторая, что она ожидает получить на вход некоторые данные.
Давайте изменим определение нашей второй процедуры, добавив в скобки имена параметров, которые она будет принимать:
Процедура Вторая(ЗначениеА, ЗначениеБ) Экспорт
// Теперь мы работаем с переданными значениями
с = ЗначениеА + ЗначениеБ;
// Здесь можно выполнять дальнейшие действия с переменной "с"
КонецПроцедуры
Теперь процедура готова принимать данные. Обратите внимание, что имена параметров (ЗначениеА, ЗначениеБ) не обязаны совпадать с именами переменных в вызывающей процедуре (а, b). Это разные сущности.
Теперь настроим вызов из первой процедуры:
Процедура Первая() Экспорт
а = 1;
b = 2;
// Передаем переменные "а" и "b" внутрь процедуры "Вторая"
Вторая(а, b);
КонецПроцедуры
Как это работает:
Вторая(а, b) платформа берет значение переменной а и помещает его в первый параметр метода Вторая (который мы назвали ЗначениеА).b помещается во второй параметр (ЗначениеБ).Вторая производятся вычисления с уже доступными данными.В 1С существует важная особенность передачи параметров. По умолчанию параметры передаются по ссылке. Давайте разберем, что это значит, на примере.
Процедура Первая()
а = 1;
ИзменитьЧисло(а);
// Чему равно "а" здесь? Оно станет равно 10!
КонецПроцедуры
Процедура ИзменитьЧисло(ВходящееЧисло)
ВходящееЧисло = 10;
КонецПроцедуры
Так как передача идет по ссылке, переменная ВходящееЧисло ссылается на ту же ячейку памяти, что и переменная а. Изменяя одну, мы меняем и другую.
Если мы хотим защитить исходные данные от случайного изменения внутри вызываемой процедуры, следует использовать ключевое слово Знач. Это создает копию переменной.
Процедура Вторая(Знач ЗначениеА, Знач ЗначениеБ) Экспорт
// Изменения переменных здесь НЕ повлияют на переменные в процедуре "Первая"
с = ЗначениеА + ЗначениеБ;
КонецПроцедуры
Использование Знач считается хорошим тоном, если процедура не должна возвращать измененные данные через параметры.
Второй способ заключается в расширении области видимости переменных. Мы можем объявить переменные в самом начале модуля (вне процедур). Такие переменные называются переменными модуля.
Перем ГлобальнаяА;
Перем ГлобальнаяБ;
Процедура Первая() Экспорт
// Мы не объявляем новые переменные, а используем объявленные выше
ГлобальнаяА = 1;
ГлобальнаяБ = 2;
Вторая();
КонецПроцедуры
Процедура Вторая() Экспорт
// Процедура "видит" переменные модуля
с = ГлобальнаяА + ГлобальнаяБ;
КонецПроцедуры
Почему этот способ работает?
Ключевое слово Перем в заголовке модуля делает эти переменные доступными для всех процедур и функций данного модуля. Они живут столько же, сколько живет сам программный модуль (например, пока открыта форма).
Почему это опасно?
Как справедливо заметили участники обсуждения, использование "глобальных" переменных модуля для передачи данных между экспортными процедурами — плохая практика. Рассмотрим причины:
Вторая вызовут из другого места программы, неизвестно, какие значения будут в переменных ГлобальнаяА и ГлобальнаяБ. Они могут быть пустыми или содержать мусор от прошлых вычислений.Вторая в другой модуль, так как она зависит от внешних переменных.Иногда нам нужно сделать процедуру универсальной, чтобы в неё можно было передавать параметры, а можно было и не передавать. В 1С это реализуется через параметры по умолчанию.
Разберем пример, где мы модифицируем нашу процедуру так, чтобы она могла работать даже без переданных значений:
Процедура Вторая(ЗначениеА = 0, ЗначениеБ = 0) Экспорт
с = ЗначениеА + ЗначениеБ;
// Если вызвать Вторая(), то с = 0 + 0
// Если вызвать Вторая(5), то с = 5 + 0
КонецПроцедуры
Конструкция = 0 задает значение по умолчанию. Если при вызове параметр пропущен, платформа подставит ноль (или любое другое указанное вами значение, например Неопределено или пустую строку).
Подводя итог нашему разбору, выделим ключевые правила для начинающего программиста 1С:
Перем) только для хранения состояния всего объекта (например, текущий контекст обработки), но не для передачи данных между двумя последовательно вызываемыми процедурами.СуммаДокумента, а не просто а), чтобы даже через полгода вы могли понять, что именно делает эта функция.Знач для параметров примитивных типов (Число, Строка, Дата, Булево), если вы не планируете менять их значение внутри процедуры и возвращать результат обратно.Следуя этим простым правилам, вы сможете избежать "каши" в коде и ошибок с видимостью переменных, характерных для этапа обучения.