Вызов процедур и функций во встроенном языке 1С 8.3: от простой строки до серверного вызова
Содержание
Функцию в 1С вызывают по имени со скобками: Результат = ПосчитатьСкидку(Сумма, 5);. Если функция лежит в другом модуле, перед именем ставят имя модуля через точку: ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Значение). Чтобы такой вызов сработал, в объявлении функции должно стоять слово Экспорт.
Дальше вопрос в деталях: из какого контекста вы вызываете (клиент или сервер), как передаются параметры и что делать, когда имя функции известно только во время работы программы. Всё это общее для платформы 8.3 и не зависит от конфигурации: правила одинаковы в БП 3.0, ЗУП 3.1, УТ 11 и в собственной разработке.
Синтаксис вызова функции в 1С: процедура, функция, точка
Во встроенном языке два вида методов:
- 📦 Процедура — выполняет действия и ничего не возвращает.
- 🔁 Функция — выполняет действия и возвращает значение через
Возврат.
Функция ПосчитатьСкидку(Сумма, Процент)
Возврат Окр(Сумма * Процент / 100, 2);
КонецФункции
Процедура ПоказатьСкидку()
Скидка = ПосчитатьСкидку(1200, 5); // 60
Сообщить("Скидка: " + Формат(Скидка, "ЧДЦ=2"));
КонецПроцедуры
Функцию можно вызвать и как процедуру, отдельной строкой: ПосчитатьСкидку(1200, 5);. Ошибки не будет, возвращённое значение просто пропадёт. Обратное не работает
: присвоить результат процедуры переменной нельзя.
Скобки обязательны даже без параметров: ОбновитьИтоги();. Без скобок платформа воспримет имя как переменную.
Точечная нотация
Через точку вызывают методы объектов и модулей:
| Что вызываем | Пример | Где описан метод |
|---|---|---|
| Метод объекта платформы | Массив.Добавить(10) | встроенный тип |
| Экспортный метод общего модуля | РаботаСФайлами.ДвоичныеДанныеФайла(Ссылка) | общий модуль |
| Экспортный метод модуля менеджера | Справочники.Номенклатура.ЗаполнитьПоУмолчанию() | модуль менеджера справочника |
| Экспортный метод модуля объекта | ДокументОбъект.ЗаполнитьТовары() | модуль объекта документа |
| Метод формы из её модуля | ОбновитьОтображение() | модуль формы, без точки |
Метод модуля объекта вызывают у объекта, а не у ссылки. Ссылка.ЗаполнитьТовары() даст ошибку, нужен Ссылка.ПолучитьОбъект().ЗаполнитьТовары().
Параметры функции в 1С: по ссылке, по значению и необязательные
По умолчанию параметр передаётся по ссылке. Функция получает ту же переменную, что лежит у вызывающего кода, и если присвоит ей новое значение, оно изменится и снаружи.
Процедура Удвоить(Число)
Число = Число * 2;
КонецПроцедуры
А = 10;
Удвоить(А);
// А = 20
Ключевое слово Знач передаёт копию значения:
Процедура Удвоить(Знач Число)
Число = Число * 2; // меняется только локальная копия
КонецПроцедуры
А = 10;
Удвоить(А);
// А = 10
Ловушка со ссылочными типами
Знач защищает переменную, но не содержимое объекта. Если передать массив или структуру с Знач, функция не сможет заменить сам массив, но спокойно добавит в него элементы:
Процедура Дополнить(Знач Список)
Список.Добавить("новый"); // изменит исходный массив
Список = Новый Массив; // на вызывающий код не повлияет
КонецПроцедуры
Нужна независимая копия коллекции — делайте её явно: Новый ФиксированныйМассив(Массив) для защиты от изменений или копирование в цикле. В БСП есть ОбщегоНазначения.СкопироватьРекурсивно().
Значения по умолчанию и пропуск параметров
Параметру можно задать значение по умолчанию. Тогда при вызове его можно не передавать:
Функция Цена(Номенклатура, Дата = Неопределено, ВидЦены = Неопределено)
Если Дата = Неопределено Тогда
Дата = ТекущаяДатаСеанса();
КонецЕсли;
// ...
КонецФункции
Цена(Товар); // Дата и ВидЦены по умолчанию
Цена(Товар, , ВидЦеныОпт); // пропущен средний параметр
Пропуск задают пустым местом между запятыми. Необязательные параметры ставьте в конец списка: так вызовы читаются проще.
Вызов функции из другого модуля: Экспорт и общие модули
Чтобы функцию видели снаружи модуля, в её объявлении ставят Экспорт:
// Общий модуль ЦеныСервер
Функция ЦенаНоменклатуры(Номенклатура, Дата) Экспорт
// ...
КонецФункции
Вызов из любого места, где доступен модуль:
Цена = ЦеныСервер.ЦенаНоменклатуры(Товар, ТекущаяДатаСеанса());
Флажки общего модуля решают, где он доступен
В свойствах общего модуля в конфигураторе стоят флажки. От них зависит, откуда функцию можно вызвать:
| Флажки модуля | Откуда вызывается | Типичное использование |
|---|---|---|
Сервер | только с сервера | запросы, запись объектов |
Сервер + Вызов сервера | с клиента, с переходом на сервер | получить данные для формы |
Клиент (управляемое приложение) | только с клиента | диалоги, оповещения |
Клиент + Сервер | в обоих контекстах | расчёты без обращения к базе |
Глобальный | без имени модуля | редко, утяжеляет запуск |
Функцию модуля с флажком Глобальный вызывают без префикса: ЦенаНоменклатуры(...). Злоупотреблять этим не стоит: глобальные модули компилируются при старте сеанса, а имена начинают пересекаться.
Модули менеджера и объекта
Экспортная функция модуля менеджера вызывается через коллекцию: Документы.РеализацияТоваровУслуг.МояФункция(). Модуль менеджера работает на сервере, с клиента напрямую его не вызвать: нужна серверная процедура формы или общий модуль с флажком Вызов сервера.
Модуль задан строкой
Если имя модуля известно только в момент выполнения, в конфигурациях на БСП есть ОбщегоНазначения.ОбщийМодуль("ИмяМодуля"). Функция возвращает сам модуль, у которого дальше вызывают метод через точку. Это безопаснее, чем собирать вызов строкой.
Клиент и сервер: директивы компиляции и вызов из формы
В модуле управляемой формы каждая процедура помечена директивой. Она определяет, где выполняется код:
- 🖥️
&НаКлиенте— на компьютере пользователя, доступны диалоги. - 🗄️
&НаСервере— на сервере, вместе с данными формы. - ⚡
&НаСервереБезКонтекста— на сервере, но без передачи формы. - 🔀
&НаКлиентеНаСервереБезКонтекста— в обоих местах, без доступа к данным базы.
Типичный вызов из кнопки:
&НаКлиенте
Процедура Пересчитать(Команда)
Итог = ПолучитьИтогНаСервере(Объект.Контрагент);
Сообщить(Итог);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьИтогНаСервере(Знач Контрагент)
Запрос = Новый Запрос("ВЫБРАТЬ ... ГДЕ Контрагент = &Контрагент");
Запрос.УстановитьПараметр("Контрагент", Контрагент);
// ...
Возврат 0;
КонецФункции
С клиента на сервер можно уйти, с сервера на клиент — нет. Серверная функция не может вызвать &НаКлиенте, показать вопрос или открыть форму.
Вызов &НаСервере передаёт на сервер и обратно все данные формы, включая табличные части. Если серверной функции нужна только ссылка на контрагента, используйте &НаСервереБезКонтекста и передайте ссылку параметром.
При вызове сервера с клиента параметры, переданные по ссылке, после выполнения отправляются обратно на клиент. Знач у серверной функции убирает этот обратный путь и экономит трафик.
Динамический вызов, рекурсия и асинхронность в 1С
Выполнить и Вычислить
Когда имя функции хранится в строке, есть два оператора:
Выполнить(Строка)— выполняет фрагмент кода как набор операторов.Вычислить(Строка)— вычисляет выражение и возвращает значение.
ИмяМетода = "ЦеныСервер.ЦенаНоменклатуры";
Цена = Вычислить(ИмяМетода + "(Товар, ДатаЦены)");
Никогда не подставляйте в Выполнить строку, которую ввёл пользователь или прислала внешняя система. Это прямой путь к выполнению чужого кода. Для внешних алгоритмов включайте УстановитьБезопасныйРежим(Истина), а в БСП используйте ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяМетода, Параметры): он проверяет имя перед вызовом.
Код внутри строки не проверяется при сохранении модуля. Опечатка всплывёт только при выполнении, у конкретного пользователя.
Рекурсия
Функция может вызывать саму себя. Классический случай — обход иерархического справочника:
Процедура ОбойтиГруппу(Родитель, Результат)
Выборка = Справочники.Номенклатура.Выбрать(Родитель);
Пока Выборка.Следующий() Цикл
Результат.Добавить(Выборка.Ссылка);
Если Выборка.ЭтоГруппа Тогда
ОбойтиГруппу(Выборка.Ссылка, Результат);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рекурсии нужно условие выхода. Без него глубина вызовов растёт, пока платформа не прервёт выполнение с ошибкой переполнения стека. Для иерархии справочника часто удобнее один запрос с условием В ИЕРАРХИИ, чем рекурсивный обход.
Лямбды: чего в 1С нет и чем заменить
Анонимных функций и лямбда-выражений во встроенном языке нет. Функцию нельзя положить в переменную как значение. Замены такие:
ОписаниеОповещения— объект, который хранит имя процедуры, модуль и дополнительные параметры. Его передают в немодальные диалоги, и платформа вызывает процедуру по завершении.- Имя метода строкой +
ОбщийМодуль()илиВыполнитьМетодКонфигурации(). - Общий модуль-«стратегия» — несколько модулей с одинаковым набором экспортных функций, нужный выбирается по условию.
&НаКлиенте
Процедура Удалить(Команда)
Оповещение = Новый ОписаниеОповещения("УдалитьЗавершение", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Удалить строку?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура УдалитьЗавершение(Ответ, ДополнительныеПараметры) Экспорт
Если Ответ = КодВозвратаДиалога.Да Тогда
// удаление
КонецЕсли;
КонецПроцедуры
Процедура-обработчик оповещения должна быть экспортной, иначе платформа её не найдёт.
Асинх и Ждать
С платформы 8.3.18 есть асинхронный синтаксис. Функцию помечают Асинх, а результат асинхронного метода ожидают через Ждать:
&НаКлиенте
Асинх Процедура Удалить(Команда)
Ответ = Ждать ВопросАсинх("Удалить строку?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
// удаление
КонецЕсли;
КонецПроцедуры
Код читается сверху вниз, без отдельной процедуры-обработчика. Асинхронная функция возвращает Обещание, поэтому её результат тоже получают через Ждать. Работает только на клиенте. В типовых конфигурациях пока чаще встречается вариант с ОписаниеОповещения, и оба нужно уметь читать.
Обработчики событий: функции, которые вызывает платформа
Часть процедур вы не вызываете сами. Их вызывает платформа, когда наступает событие. Имя и параметры у таких процедур фиксированы:
| Событие | Где | Когда вызывается |
|---|---|---|
ПередЗаписью(Отказ) | модуль объекта | перед записью в базу |
ОбработкаПроведения(Отказ, Режим) | модуль документа | при проведении |
ОбработкаЗаполнения(ДанныеЗаполнения, ...) | модуль объекта | при создании и вводе на основании |
ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) | модуль формы | при открытии формы |
ПриОткрытии(Отказ) | модуль формы | на клиенте после создания |
Остановить действие можно через параметр Отказ: Отказ = Истина; в ПередЗаписью отменит запись. Это и есть пример параметра по ссылке, через который процедура возвращает результат вызывающему.
Обработчики элементов формы и команд связывают с процедурами в палитре свойств: у поля на вкладке событий выбирают событие и создают обработчик кнопкой с лупой. Платформа сама создаст процедуру с правильными параметрами.
Подписки на события
Чтобы повесить свою функцию на запись многих объектов, не трогая их модули, используют подписку на события в ветке Общие → Подписки на события. В подписке указывают источники, событие и обработчик — экспортную процедуру общего модуля. Так типовой код остаётся без изменений, что упрощает обновление.
Как ускорить вызов функций в 1С
Сам вызов функции стоит дёшево. Время уходит на то, что происходит внутри и между клиентом и сервером.
- 🔄 Объединяйте серверные вызовы. Три вызова сервера подряд из одной кнопки — три сетевых обращения. Соберите всё в одну серверную функцию, возвращающую структуру.
- ⚡
&НаСервереБезКонтекставместо&НаСервере, если форма на сервере не нужна. - 💾 Кэш повторного использования. У общего модуля в свойстве
Повторное использование возвращаемых значенийставятНа время сеансаилиНа время вызова. Функция с одинаковыми параметрами второй раз возьмёт результат из кэша. Подходит для настроек и констант, не подходит для меняющихся данных. - 🗂️ Запрос вместо цикла с обращениями через точку.
Товар.Родитель.Наименованиев цикле читает объекты из базы. Один запрос по всем строкам быстрее. - 🧹 Не вызывайте
Вычислитьв цикле. Строку разбирают при каждом выполнении. Определите метод один раз до цикла.
Найти медленную функцию помогает замер производительности в отладчике: в конфигураторе при отладке он включается из меню Отладка. Отчёт покажет строки и вызовы, которые съели больше всего времени.
Ошибки при вызове функции и что они означают
Ещё одна тихая проблема: функция отрабатывает без ошибки, но значение не возвращает. Обычно в одной из ветвей Если нет Возврат. Тогда функция возвращает Неопределено, и ошибка вылезает дальше по коду.