Как устроены процедуры во встроенном языке 1С
Содержание
Процедура в 1С — это именованный блок встроенного языка, который выполняет действие и ничего не возвращает в точку вызова. Объявляется парой операторов Процедура … КонецПроцедуры, вызывается по имени как отдельная строка кода. Параметры передаются в круглых скобках, по умолчанию — по ссылке.
Главное отличие от функции: процедура не отдаёт результат оператором Возврат Значение, поэтому её нельзя подставить в выражение или присвоить переменной. Если код должен что-то посчитать и вернуть — нужна функция. Если просто выполнить шаги (заполнить таблицу, открыть форму, записать объект) — процедура.
Ниже разберём синтаксис объявления, передачу параметров с модификатором Знач, видимость через Экспорт, директивы компиляции &НаКлиенте / &НаСервере и ошибки, на которых чаще всего спотыкаются.
Чем процедура отличается от функции
Обе конструкции — подпрограммы встроенного языка. Разница в одном: функция обязана вернуть значение, процедура — нет.
| Признак | Процедура | Функция |
|---|---|---|
| Ключевые слова | Процедура … КонецПроцедуры | Функция … КонецФункции |
| Возврат значения | нет | обязателен, оператор Возврат <значение> |
| Как вызывают | отдельной строкой: Пересчитать(); | в выражении: Итог = Сумма(А, Б); |
| Типичное имя | глагол: ЗаполнитьТаблицу | существительное или вопрос: СуммаДокумента, ЭтоРабочийДень |
💡 Функцию разрешено вызвать и как процедуру — просто отдельной строкой, проигнорировав результат. А вот процедуру в выражение подставить нельзя: получите ошибку «Обнаружена попытка использования процедуры как функции».
Обработчики событий форм и объектов (ПриОткрытии, ПередЗаписью, ОбработкаПроведения) — всегда процедуры. Платформа вызывает их сама и результата от них не ждёт.
Синтаксис объявления и вызова
Минимальная процедура выглядит так:
Процедура Поприветствовать()
Сообщить("Готово");
КонецПроцедуры
Вызов — имя со скобками отдельной строкой:
Поприветствовать();
Имя подчиняется правилам идентификаторов: буквы, цифры и подчёркивание, начинается не с цифры. Регистр не важен — Поприветствовать и ПОПРИВЕТСТВОВАТЬ для платформы одно и то же, но в типовых решениях принят «ВерблюжийРегистр».
Выйти из процедуры досрочно можно оператором Возврат без значения:
Процедура ОбработатьСтроку(Данные)
Если Не ЗначениеЗаполнено(Данные) Тогда
Возврат; // дальше идти незачем
КонецЕсли;
// основная логика
КонецПроцедуры
Процедуры нельзя объявлять внутри других процедур — вложенности подпрограмм в языке 1С нет. Все процедуры объявляются на верхнем уровне модуля. Локальные переменные внутри объявляются оператором Перем в самом начале тела, до первого исполняемого оператора.
Как передавать параметры: по ссылке и по значению
Параметры перечисляются в скобках через запятую, порядок при вызове должен совпадать с порядком объявления. Жёсткого ограничения на их число нет, но если параметров набирается больше пяти-шести — это сигнал передавать структуру с именованными полями: читать такой вызов проще, а добавить новое поле можно, не трогая сигнатуру.
По умолчанию параметры передаются по ссылке: если внутри процедуры присвоить параметру новое значение, изменится и переменная вызывающего кода.
Процедура Удвоить(Число)
Число = Число * 2;
КонецПроцедуры
Х = 5;
Удвоить(Х);
Сообщить(Х); // выведет 10
Чтобы защититься от этого, ставят модификатор Знач — параметр принимается по значению, то есть копией:
Процедура Удвоить(Знач Число)
Число = Число * 2; // меняется только копия
КонецПроцедуры
Х = 5;
Удвоить(Х);
Сообщить(Х); // по-прежнему 5
Важная тонкость: Знач копирует ссылку, а не сам объект. Для коллекций — Массив, Структура, Соответствие, ТаблицаЗначений — даже с Знач вы работаете с тем же объектом в памяти. Добавление элемента в массив будет видно снаружи, потому что копируется ссылка на массив, а не его содержимое. Знач страхует только от переприсваивания самого параметра.
Процедура ДобавитьЭлемент(Знач Список)
Список.Добавить("новый"); // изменит исходный массив!
КонецПроцедуры
Значения параметров по умолчанию
Хвостовым параметрам можно задать значение по умолчанию через =. Тогда при вызове их можно не указывать:
Процедура Записать(Объект, ВыводитьСообщение = Истина)
Объект.Записать();
Если ВыводитьСообщение Тогда
Сообщить("Записано");
КонецЕсли;
КонецПроцедуры
Записать(Договор); // ВыводитьСообщение = Истина
Записать(Договор, Ложь); // тихо, без сообщения
Ограничения, о которые часто спотыкаются:
- 📌 значением по умолчанию может быть только литерал — число, строка, булево, дата,
Неопределено. Выражение или вызов функции туда не поставить; - 📌 параметры со значением по умолчанию идут только в конце списка. Нельзя задать умолчание среднему параметру, а следующему — нет;
- 📌 «пропустить» параметр в середине, оставив запятые, в 1С нельзя — только отбросить хвост.
Видимость: локальные и экспортные процедуры
По умолчанию процедура видна только внутри своего модуля. Чтобы вызвать её из другого модуля, добавьте Экспорт после скобок:
Процедура ПересчитатьИтоги() Экспорт
// ...
КонецПроцедуры
Где лежит процедура — определяет, откуда её видно:
- 🧩 Общий модуль — процедуры с
Экспортдоступны по всей конфигурации через имя модуля:РаботаСДокументами.ПересчитатьИтоги(). Это основное место для переиспользуемого кода. - 🧩 Модуль объекта (документа, справочника) — экспортные процедуры вызываются через ссылку или объект:
Документ.ПроверитьЗаполнение(). - 🧩 Модуль формы — экспортные процедуры доступны из других форм и команд, но контекст у них свой.
- 🧩 Модуль менеджера — удобен для процедур, относящихся к объекту в целом, а не к конкретному экземпляру.
Без Экспорт процедура остаётся приватной — это нормально и правильно для вспомогательной логики, которую не нужно открывать наружу. Помечать экспортом всё подряд — плохая практика: чем меньше внешних точек входа, тем проще менять внутреннюю реализацию модуля, не ломая вызывающий код.
💡 Имя процедуры — часть её документации. Для общих модулей в типовых решениях принят префикс области ответственности, а само имя — глагол в неопределённой форме: ЗаполнитьРеквизиты, ПроверитьПрава, ОтправитьУведомление. По такому имени видно, что процедура делает, ещё до чтения тела.
Директивы компиляции: клиент и сервер
В управляемом приложении код исполняется на двух сторонах — на клиенте и на сервере. Поэтому перед процедурами в модулях форм и команд ставят директивы компиляции, которые говорят платформе, где выполнять код:
&НаКлиенте— на клиенте. Отсюда доступны интерактивные методы:ОткрытьФорму,Сообщить,Вопрос, обращение к элементам формы. Прямого доступа к базе данных нет.&НаСервере— на сервере, с полным контекстом формы. Доступны чтение и запись объектов, запросы. Интерактив недоступен.&НаСервереБезКонтекста— на сервере, но без данных формы. Работает быстрее и экономит трафик, потому что контекст формы на сервер не передаётся.&НаКлиентеНаСервереБезКонтекста— компилируется в оба места; подходит для чистых вычислений без обращения к базе и формам.
&НаКлиенте
Процедура ПересчитатьНажатие(Команда)
ПересчитатьНаСервере(); // вызов серверной процедуры
КонецПроцедуры
&НаСервере
Процедура ПересчитатьНаСервере()
// здесь доступны данные формы и запросы
КонецПроцедуры
Клиент не может напрямую вызвать произвольную серверную процедуру. Разрешён вызов &НаСервере-процедур своей формы и экспортных процедур общих модулей с установленным флагом «Вызов сервера». Попытка иначе обратиться к серверу с клиента даёт ошибку компиляции.
В общих модулях директивы перед процедурами не ставят — там сторону исполнения задают флаги самого модуля в палитре свойств: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера», «Внешнее соединение». Исключение — объединённый клиент-серверный модуль, где директивы перед процедурами допустимы.
💡 Правило экономии: тяжёлые вычисления и работу с базой выносите в &НаСервереБезКонтекста, а серверных вызовов делайте как можно меньше — каждый вызов это передача данных по сети и накладные расходы.
Процедуры-обработчики событий
Большая часть процедур в прикладном решении — это обработчики событий. Платформа вызывает их сама в определённый момент: при открытии формы, перед записью объекта, при проведении документа. Такие процедуры всегда без возвращаемого значения, то есть именно процедуры, и получают параметры от платформы.
Привязка обработчика зависит от того, где он живёт:
- 🔌 Модуль объекта или формы — обработчик с предопределённым именем платформа находит сама. Например, процедуру
ПередЗаписьюв модуле объекта система вызовет перед каждой записью. Создавать такие обработчики удобно из палитры свойств объекта по значку с лупой напротив события. - 🔌 Элемент формы — обработчик назначается в свойствах элемента (поля, кнопки, таблицы). Платформа создаёт процедуру с директивой
&НаКлиентеи прописывает её имя в свойстве события.
&НаКлиенте
Процедура СуммаПриИзменении(Элемент)
РассчитатьИтогНаСервере();
КонецПроцедуры
У обработчиков фиксированный набор параметров — его задаёт платформа, менять нельзя. Если параметр не нужен, его всё равно оставляют в объявлении. Удалять обработчик следует не только из модуля, но и из свойства события, иначе при вызове получите ошибку «Процедура не обнаружена».
Процедура может вызывать сама себя — рекурсия в языке 1С разрешена. Это удобно при обходе иерархии: процедура обрабатывает узел и для каждого дочернего вызывает себя же.
Процедура ОбойтиСтроки(Знач Строки)
Для Каждого Строка Из Строки Цикл
ОбработатьСтроку(Строка);
Если Строка.Строки.Количество() > 0 Тогда
ОбойтиСтроки(Строка.Строки); // спуск на уровень ниже
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Условие выхода здесь — отсутствие дочерних строк. Без такого условия рекурсия уйдёт в бесконечность и платформа аварийно завершит работу с переполнением стека.
Проверьте себя
Частые ошибки при работе с процедурами
- 🐞 Забыли
Экспорт— процедура не видна из другого модуля, платформа сообщает «Процедура не определена». Решение: добавитьЭкспортпосле скобок и для общего модуля вызывать через имя модуля. - 🐞 Смешали стороны исполнения — попытка обратиться к элементам формы из
&НаСервереБезКонтекстаили вызватьОткрытьФормуна сервере. Разнесите интерактив на клиент, работу с базой на сервер. - 🐞 Выражение в значении по умолчанию —
Процедура П(Дата = ТекущаяДата())не скомпилируется. ПоставьтеНеопределено, а внутри подставьте значение, если параметр пуст. - 🐞 Неожиданное изменение переменной — передали параметр по ссылке и присвоили ему новое значение внутри. Если менять исходную переменную не нужно — ставьте
Знач. - 🐞 Крупные коллекции между клиентом и сервером — передача большой таблицы значений туда-обратно сериализуется и тормозит. Собирайте данные на той стороне, где они нужны.
- 🐞 Процедура вместо функции — попытались подставить процедуру в выражение. Если нужен результат — объявляйте функцию с
Возврат.
Хороший стиль: одна процедура решает одну задачу, имя начинается с глагола и отражает действие, а всё, что должно быть доступно снаружи, помечено Экспорт осознанно, а не «на всякий случай».
Частые вопросы
Чем вызов процедуры отличается от вызова функции в коде?
Пересчитать();. Функцию — в выражении, присваивая результат: Итог = Сумма(А, Б);. Функцию допустимо вызвать и отдельной строкой, проигнорировав результат, а процедуру в выражение подставить нельзя.Как передать в процедуру несколько значений обратно?
Знач, и присвоенные внутри значения увидит вызывающий код. Для коллекций изменения видны всегда, даже со Знач, потому что копируется ссылка, а не сам объект.Зачем нужны директивы `&НаКлиенте` и `&НаСервере`?
Можно ли задать параметру значение по умолчанию?
= после имени: Процедура П(Флаг = Истина). Но только литералом (число, строка, булево, дата, Неопределено) и только для последних параметров списка. Выражение или вызов функции в умолчание поставить нельзя.Где объявлять процедуру, чтобы вызывать её из разных мест конфигурации?
Экспорт и выбрав флаги модуля под нужную сторону исполнения. Вызов — через имя модуля: ОбщийМодуль.ПроцедураЭкспорт(). Это стандартное место для переиспользуемого кода.