Как устроена процедура во встроенном языке 1С
Содержание
Процедура в 1С — это именованный блок кода, который выполняет действия, но ничего не возвращает в точку вызова. Объявляется парой Процедура … КонецПроцедуры, вызывается по имени со скобками. От функции отличается ровно одним: функция возвращает значение через Возврат, процедура — нет.
Ниже — синтаксис объявления, как работают модификатор Экспорт и общий модуль со свойством «Глобальный», чем передача Знач отличается от передачи по ссылке, когда писать процедуру, а когда функцию, и какие ошибки чаще всего тормозят код. Примеры — на встроенном языке платформы 8.3.
Синтаксис объявления и первый вызов
Минимальная процедура выглядит так:
Процедура ПоказатьСообщение()
Сообщить("Готово");
КонецПроцедуры
Вызов — имя и круглые скобки, даже если параметров нет:
ПоказатьСообщение();
Разберём элементы объявления:
- 🔹 ключевое слово
Процедураоткрывает блок,КонецПроцедуры— закрывает; - 🔹 после имени обязательны круглые скобки — в них перечисляются параметры;
- 🔹 тело располагается между открывающей и закрывающей строкой;
- 🔹 точка с запятой после
КонецПроцедурыне ставится.
Имя процедуры пишут без пробелов, обычно в стиле «КаждоеСловоСБольшойБуквы». Имена методов, встроенных в платформу, занимать нельзя — транслятор выдаст конфликт.
Локальные переменные, если они нужны, объявляют оператором Перем в самом начале тела, до первого исполняемого оператора:
Процедура ПосчитатьИтог(Таблица)
Перем Итог;
Итог = 0;
Для Каждого Строка Из Таблица Цикл
Итог = Итог + Строка.Сумма;
КонецЦикла;
Сообщить("Итого: " + Итог);
КонецПроцедуры
Явное объявление Перем не обязательно — переменная создаётся и при первом присваивании. Комментарии в коде начинаются с двух слэшей // и помогают коллегам понять назначение процедуры.
Внутри тела можно досрочно выйти оператором Возврат без значения — остаток кода выполнен не будет:
Процедура ОбработатьСтроку(Строка)
Если Строка = "" Тогда
Возврат;
КонецЕсли;
// основная логика только для непустой строки
Сообщить("Обрабатываю: " + Строка);
КонецПроцедуры
Ранний Возврат в начале процедуры (так называемая «защитная проверка») читается легче, чем вся логика, завёрнутая в один большой Если … КонецЕсли.
Модификатор Экспорт и общий модуль «Глобальный»
По умолчанию процедура видна только внутри своего модуля. Чтобы её можно было вызвать снаружи, после списка параметров ставят ключевое слово Экспорт:
Процедура ЗаписатьОтметку(Текст) Экспорт
Сообщить(ТекущаяДата() + ": " + Текст);
КонецПроцедуры
Где именно станет доступна экспортная процедура — зависит от типа модуля:
| Модуль | Как вызвать экспортную процедуру |
|---|---|
| Общий модуль | ИмяМодуля.ЗаписатьОтметку("…") |
| Модуль менеджера объекта | Справочники.Номенклатура.Метод() |
| Модуль формы | через реквизит формы, из своего контекста |
А теперь про «Глобальный». Это не ключевое слово процедуры, а свойство общего модуля. Открываете общий модуль в конфигураторе, в палитре свойств включаете флаг Глобальный — и все экспортные процедуры этого модуля становятся доступны во встроенном языке без указания имени модуля:
// обычный общий модуль:
РаботаСТекстом.ОчиститьПробелы(Строка);
// глобальный общий модуль — имя модуля не нужно:
ОчиститьПробелы(Строка);
За удобство глобального модуля платишь памятью: он загружается при старте сеанса и висит в памяти всё время работы. Поэтому в глобальные модули выносят только то, что реально нужно повсюду, а остальное оставляют в обычных модулях с префиксом.
Имена экспортных процедур в глобальных модулях не должны пересекаться между собой и со встроенными методами платформы — иначе получите неоднозначность вызова.
Где живут процедуры: типы модулей
Процедура всегда принадлежит какому-то модулю, и от типа модуля зависит, что ей доступно и кто её может вызвать. Основные места, где вы будете писать процедуры:
- 🔹 Модуль формы — обработчики элементов и команд:
ПриИзмененииПоля, нажатия кнопок, открытие и закрытие формы. Здесь обязательны директивы компиляции. - 🔹 Модуль объекта (справочника, документа) — события самого объекта:
ПриЗаписи,ОбработкаПроведенияу документа,ОбработкаЗаполнения. Процедуры видят реквизиты объекта напрямую. - 🔹 Модуль менеджера — методы, относящиеся к объекту в целом, а не к одному экземпляру; удобны для экспортных функций-«конструкторов».
- 🔹 Общий модуль — переиспользуемые процедуры и функции. Флаги модуля (Клиент, Сервер, Вызов сервера, Глобальный) определяют контекст и видимость.
- 🔹 Модуль приложения и модуль сеанса — события запуска и завершения системы.
Простое правило: код, который нужен в нескольких местах, выносите в общий модуль с Экспорт. Код, привязанный к конкретной форме или объекту, держите в их собственных модулях — так его проще сопровождать.
Если одна и та же процедура понадобилась и на клиенте, и на сервере, часто её дублируют в двух общих модулях (клиентском и серверном) либо выносят расчётную часть в серверный модуль с флагом «Вызов сервера».
Параметры: по ссылке и по значению (Знач)
Параметры в 1С по умолчанию передаются по ссылке. Это значит, что изменение параметра внутри процедуры меняет саму переменную вызывающего кода:
Процедура Обнулить(Сумма)
Сумма = 0;
КонецПроцедуры
Итого = 500;
Обнулить(Итого);
// теперь Итого = 0
Если дописать перед параметром ключевое слово Знач, он передаётся по значению — процедура получает копию, а оригинал остаётся нетронутым:
Процедура Обнулить(Знач Сумма)
Сумма = 0;
КонецПроцедуры
Итого = 500;
Обнулить(Итого);
// Итого по-прежнему 500
Есть тонкость с объектами. Знач защищает от переприсваивания самой переменной, но не «замораживает» содержимое ссылочного объекта. Если в процедуру передали таблицу значений с модификатором Знач, присвоить Таблица = Новый ТаблицаЗначений внутри уже не получится повлиять на оригинал, а вот Таблица.Добавить() всё равно добавит строку в исходную таблицу — вы работаете с тем же объектом.
Параметрам можно задавать значения по умолчанию через знак равенства. Такие параметры становятся необязательными при вызове:
Процедура Логировать(Текст, Уровень = "Инфо")
Сообщить("[" + Уровень + "] " + Текст);
КонецПроцедуры
Логировать("Старт"); // Уровень = "Инфо"
Логировать("Сбой", "Ошибка"); // Уровень = "Ошибка"
- ✅ параметры со значением по умолчанию ставьте в конце списка;
- ✅ большие коллекции без необходимости не передавайте через
Знач— создание копии на тысячах строк заметно тормозит; - ✅ если процедура не должна портить входные данные,
Значдля простых типов (число, строка, дата) — хорошая страховка.
Отдельно про число параметров. Если их набралось шесть и больше, список становится нечитаемым, а вызов — легко перепутать местами аргументы. В таких случаях передают одну Структуру с именованными полями: вызов остаётся наглядным, а добавить новый параметр можно, не меняя сигнатуру процедуры.
Параметры = Новый Структура;
Параметры.Вставить("Дата", ТекущаяДата());
Параметры.Вставить("Комментарий", "Загрузка");
ЗаписатьОтметку(Параметры);
Чем процедура отличается от функции
Разница одна, но принципиальная: функция возвращает значение, процедура — нет. Отсюда и всё остальное.
// Процедура: выполнила действие и завершилась
Процедура ВывестиИтог(Сумма)
Сообщить("Итого: " + Сумма);
КонецПроцедуры
// Функция: вернула результат через Возврат
Функция СуммаСНДС(Сумма, Ставка = 20)
Возврат Сумма + Сумма * Ставка / 100;
КонецФункции
Для суммы 1000 при ставке 20% функция вернёт 1000 + 1000 * 20 / 100 = 1200. Результат можно присвоить переменной или использовать в выражении:
Итог = СуммаСНДС(1000); // 1200
Короткая шпаргалка, что выбрать:
| Что нужно | Процедура или функция |
|---|---|
| Вывести сообщение, записать данные, провести документ | процедура |
| Посчитать и вернуть число, строку, ссылку | функция |
| Проверить условие и вернуть Истина/Ложь | функция |
| Изменить переданный параметр по ссылке | процедура (или функция) |
Внутри функции Возврат обязателен хотя бы на одной ветке и несёт значение: Возврат Результат;. Если функция завершится без Возврат со значением, она вернёт Неопределено — это частый источник трудноуловимых ошибок.
Если метод «и считает, и выводит», обычно его стоит разделить на функцию-расчёт и процедуру-вывод. Так расчёт можно переиспользовать и покрыть проверками.
Есть ещё договорённость по именам, которая облегчает чтение чужого кода. Функции обычно называют существительным-результатом (СуммаДокумента, ТекстОшибки) или с вопросительной частицей для булевых (ЭтоГруппа, ДоступноРедактирование). Процедуры называют глаголом-действием (ЗаписатьДанные, ЗаполнитьТаблицу, ОчиститьКэш). Прочитав имя, коллега сразу понимает, вернёт метод значение или просто что-то сделает.
Где процедура видна: контекст клиента и сервера
В модулях управляемых форм перед объявлением процедуры ставят директиву компиляции — она говорит, в каком контексте выполняется код:
&НаКлиенте
Процедура ПриИзмененииПоля(Элемент)
// выполняется на клиенте
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьРеквизиты()
// выполняется на сервере, видит реквизиты формы
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ЗагрузитьСписок(Параметры)
// на сервере, без доступа к данным формы — самый лёгкий вызов
КонецПроцедуры
Коротко про директивы:
- 🔹
&НаКлиенте— код на стороне пользователя: обработчики интерфейса, вопросы, открытие форм; - 🔹
&НаСервере— код на сервере с доступом к реквизитам формы; каждый вызов тянет форму на сервер и обратно; - 🔹
&НаСервереБезКонтекста— серверный код без данных формы, самый дешёвый по трафику; - 🔹 в общих модулях контекст задаётся флагами самого модуля (Клиент/Сервер), директива не нужна.
Каждый переход «клиент → сервер» — это сетевой вызов. Если в цикле на клиенте дёргать серверную процедуру тысячу раз, форма будет «задумываться». Правильнее собрать данные и сделать один серверный вызов, передав в него всё нужное параметрами.
Важно и то, какие данные переходят границу клиент–сервер. На клиент нельзя передать объект (например, СправочникОбъект) — туда «пролезают» только значения: число, строка, дата, ссылка, а также простые коллекции. Поэтому процедуры &НаСервереБезКонтекста обычно принимают и возвращают ссылки, примитивные значения или структуры, а тяжёлую работу с объектами ведут целиком на сервере.
Вне форм действуют свои правила видимости: процедура без Экспорт живёт в пределах модуля; с Экспорт в общем модуле — доступна по имени модуля; в глобальном — напрямую.
Рекурсия и примеры из практики
Процедура (и функция) может вызывать сама себя — это рекурсия. Классический пример — факториал на функции:
Функция Факториал(Н)
Если Н <= 1 Тогда
Возврат 1;
КонецЕсли;
Возврат Н * Факториал(Н - 1);
КонецФункции
Факториал(5) развернётся в 5 * 4 * 3 * 2 * 1 = 120. Главное в любой рекурсии — условие выхода: ветка, на которой вызов себя прекращается. Здесь это Н <= 1.
Процедуры рекурсивно обходят древовидные данные — например, группы и подгруппы справочника или строки дерева значений:
Процедура ОбойтиСтроки(Строки)
Для Каждого Строка Из Строки Цикл
Сообщить(Строка.Наименование);
ОбойтиСтроки(Строка.Строки); // спуск на уровень ниже
КонецЦикла;
КонецПроцедуры
Рекурсия без условия выхода уходит в бесконечность и упирается в ограничение платформы на глубину вложенных вызовов — сеанс завершится ошибкой. Для плоских коллекций обычный цикл Для Каждого проще и быстрее рекурсии.
Когда при обходе дерева нужно что-то накопить (список, сумму, таблицу), удобно завести параметр-накопитель и передавать его по ссылке на каждый уровень вложенности. Тогда все ветки рекурсии дописывают результат в один и тот же объект:
Процедура СобратьНаименования(Строки, Результат)
Для Каждого Строка Из Строки Цикл
Результат.Добавить(Строка.Наименование);
СобратьНаименования(Строка.Строки, Результат);
КонецЦикла;
КонецПроцедуры
Здесь Результат (например, массив) создаётся один раз вызывающим кодом, а процедура наполняет его по мере спуска. Рекурсия оправдана именно на деревьях заранее неизвестной глубины — для фиксированной вложенности почти всегда достаточно обычного цикла.
Пример защитной процедуры с ранним выходом и необязательным параметром — типичная заготовка для прикладного кода:
Процедура ПроверитьЗаполнение(Значение, ИмяПоля = "поле")
Если ЗначениеЗаполнено(Значение) Тогда
Возврат;
КонецЕсли;
Сообщить("Не заполнено: " + ИмяПоля);
КонецПроцедуры
Частые ошибки и как ускорить код
Разберём то, что реально ломает работу или тормозит выполнение.
- Забыли
Экспорт. Процедуру вызывают из другого модуля, а транслятор пишет «процедура не обнаружена». Проверьте, стоит лиЭкспортпосле списка параметров. - Присвоили значение параметру с
Значи ждёте изменений снаружи. Так не работает : копия изменится, оригинал — нет. Либо уберитеЗнач, либо верните результат функцией. - Функция без
Возвратсо значением. ВернётсяНеопределено, а дальше по коду — ошибка сравнения или сложения типов. Убедитесь, что значение возвращается на всех ветках. - Передача больших таблиц и списков через
Знач. Платформа копирует коллекцию целиком. На больших объёмах это ощутимая задержка — передавайте по ссылке, если копия не нужна. - Серверные вызовы в цикле на клиенте. Каждый вызов
&НаСервере-процедуры из клиентского цикла — отдельный обмен с сервером. Соберите данные и вызовите сервер один раз. - Рекурсия без условия выхода. Бесконечный спуск упрётся в предел глубины вызовов и оборвёт сеанс.
Приёмы, которые ускоряют и упрощают код:
- ⚡ выносите повторяющийся код в отдельную процедуру — дублирование убирается, править нужно в одном месте;
- ⚡ для вычислений используйте функции, для действий — процедуры: код становится предсказуемым;
- ⚡ минимизируйте число переходов клиент–сервер, передавая данные пачкой через параметры;
- ⚡ для простых типов применяйте
Значкак защиту входных данных — риск случайно испортить переменную вызывающего кода пропадает; - ⚡ держите одну процедуру ответственной за одно действие: короткий метод проще прочитать, повторно использовать и отладить, чем «простыню» на сто строк с десятком
Если.
Хорошая процедура читается как одно понятное предложение: взяли вход, выполнили действие, при необходимости сообщили результат. Если при чтении приходится держать в голове с десяток переменных и перескакивать по вложенным условиям, это сигнал разбить её на несколько мелких, каждая со своим именем-глаголом. Такой код переживает обновления конфигурации и смену разработчика гораздо спокойнее.
Частые вопросы
Можно ли из процедуры вернуть значение?
Возврат Значение;. В процедуре Возврат допустим только без значения — для досрочного выхода.Чем `Знач` отличается от обычного параметра?
Знач передаёт копию — оригинальная переменная вызывающего кода не меняется.«Глобальный» — это ключевое слово процедуры?
Почему процедура из общего модуля «не обнаружена»?
Экспорт после параметров либо не совпадает контекст компиляции общего модуля (Клиент/Сервер) с местом вызова.Процедура или функция для проверки условия?
Истина/Ложь, результат удобно использовать прямо в Если … Тогда. Процедура для такой задачи не подходит — ей нечем ответить.