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