Процедуры и функции в 1С: синтаксис, параметры, экспорт и общие модули
Процедуры и функции — основа любого кода в 1С. В статье — как они объявляются, в чём отличие функции от процедуры, как работают параметры и значения по умолчанию, что дают Экспорт и флаги общих модулей, как использовать рекурсию и когда уместны Выполнить() и Вычислить(). Каждый пример можно скопировать в свою конфигурацию.
Задача: разбить код на процедуры и функции
Любой код на встроенном языке 1С живёт внутри процедур и функций. Они позволяют не копировать одни и те же строки по десять раз, давать операциям понятные имена и вызывать код из других модулей. Разницу между ними новички часто путают, а ошибки с параметрами, областью видимости и серверным вызовом встречаются и у опытных разработчиков.
В статье — синтаксис функции в 1С и процедуры, параметры по ссылке и по значению, необязательные параметры, ключевое слово Экспорт, общие модули и их флаги, рекурсия, методы объектов и глобального контекста, а также динамическое выполнение кода через Выполнить() и Вычислить().
Процедура и функция: в чём отличие
Главное различие одно: функция возвращает значение, процедура — нет. Из-за этого функцию можно использовать в выражении, присваивать результат переменной или передавать другой функции.
Процедура ВывестиСообщение(Текст)
Сообщить(Текст);
КонецПроцедуры
Функция СуммаСНалогом(Сумма, СтавкаНДС = 20)
Возврат Сумма * (1 + СтавкаНДС / 100);
КонецФункции
// Вызов
ВывестиСообщение("Расчёт выполнен");
Итого = СуммаСНалогом(1000); // 1200
Итого = СуммаСНалогом(1000, 10); // 1100
Тело подпрограммы заканчивается ключевым словом КонецПроцедуры или КонецФункции. Оператор Возврат в функции обязателен с выражением, а в процедуре используется без значения для досрочного выхода. Если в функции Возврат не выполнился, результатом будет Неопределено. Важное практическое правило: функция не должна «молча» менять состояние системы. Если подпрограмма пишет в базу или выводит сообщения, оформляйте её как процедуру или назовите так, чтобы побочный эффект был виден из имени.
| Признак | Процедура | Функция |
|---|---|---|
| Возвращает значение | Нет | Да, через Возврат |
| Можно использовать в выражении | Нет | Да |
Возврат | Без значения, для выхода | Со значением |
| Типичное применение | Действие: запись, вывод, обработка | Вычисление, проверка, получение данных |
Параметры: по ссылке и по значению
По умолчанию параметры передаются по ссылке. Для простых типов (число, строка, дата) это означает, что присваивание внутри подпрограммы изменяет переменную вызывающего кода. Объекты (структура, массив, таблица значений) всегда передаются по ссылке на уровне содержимого: изменение элементов изменяет исходный объект. Чтобы передать значение, перед параметром ставят Знач.
Процедура УвеличитьПоСсылке(Число)
Число = Число + 1;
КонецПроцедуры
Процедура УвеличитьПоЗначению(Знач Число)
Число = Число + 1;
КонецПроцедуры
Значение = 5;
УвеличитьПоСсылке(Значение); // Значение = 6
УвеличитьПоЗначению(Значение); // Значение остаётся 6
Параметр со значением по умолчанию объявляют так: Параметр = Значение. По умолчанию допускаются только константы — число, строка, дата, Истина, Ложь, Неопределено. Вызов выражения или функции в значении по умолчанию невозможен. Необязательные параметры должны идти в конце списка, а при вызове пропущенный параметр отмечают запятой:
Функция ПолучитьСтроку(Значение, Разделитель = ";", Префикс = "")
Возврат Префикс + Строка(Значение) + Разделитель;
КонецФункции
Результат = ПолучитьСтроку(5); // "5;"
Результат = ПолучитьСтроку(5, , "№"); // "№5;"
Несколько значений: Структура и выходные параметры
Функция в 1С возвращает одно значение. Если нужно вернуть несколько, самый читаемый способ — структура. Вызывающий код получает результат с именованными полями и не должен помнить порядок.
Функция РазобратьАдрес(Адрес)
Результат = Новый Структура("Город, Улица, Дом", "", "", "");
Части = СтрРазделить(Адрес, ",", Ложь);
Если Части.Количество() > 0 Тогда
Результат.Город = СокрЛП(Части[0]);
КонецЕсли;
Если Части.Количество() > 1 Тогда
Результат.Улица = СокрЛП(Части[1]);
КонецЕсли;
Если Части.Количество() > 2 Тогда
Результат.Дом = СокрЛП(Части[2]);
КонецЕсли;
Возврат Результат;
КонецФункции
Данные = РазобратьАдрес("Казань, Баумана, 12");
Сообщить(Данные.Город + ", " + Данные.Улица);
Второй способ — выходной параметр: переменная, переданная в подпрограмму без Знач, получает новое значение. Так обычно возвращают признак успеха вместе с основным результатом: функция возвращает Истина/Ложь, а данные кладёт в параметр. Но структура универсальнее: её можно передавать дальше и расширять без изменения сигнатуры.
Экспорт и общие модули
Слово Экспорт в конце заголовка делает подпрограмму доступной за пределами модуля. Без него процедура или функция видна только внутри своего модуля. В общем модуле экспортные методы вызываются через имя модуля: ОбщегоНазначенияКлиентСервер.ЗначениеВМассив(...). В модуле объекта и модуле формы экспортные процедуры становятся методами объекта или формы.
// Общий модуль «РасчетыСервер»
Функция СтавкаНДС(Номенклатура) Экспорт
Возврат Номенклатура.СтавкаНДС;
КонецФункции
Функция ВспомогательнаяФункция() // видна только внутри модуля
Возврат 1;
КонецФункции
// Вызов из любого другого модуля
Ставка = РасчетыСервер.СтавкаНДС(Товар);
Где выполняется код общего модуля, определяется его флагами:
- Сервер — модуль доступен на сервере; Клиент (управляемое приложение) — в тонком и веб-клиенте; Внешнее соединение — при подключении через COM.
- Вызов сервера — разрешает вызывать экспортные методы серверного модуля из клиентского кода. Без этого флага клиентский метод не сможет обратиться к серверному общему модулю напрямую.
- Привилегированный — код выполняется без проверки прав; используется для служебных операций и требует осторожности.
- Повторное использование возвращаемых значений — «На время вызова» или «На время сеанса». Платформа кэширует результат функции по набору параметров и не выполняет её повторно. Подходит для справочной информации, которая не меняется в течение сеанса.
Кэшируемые функции не должны иметь побочных эффектов и зависеть от изменяемых данных, иначе пользователь увидит устаревший результат. Клиентский код вызывает серверные общие модули только при установленном флаге «Вызов сервера»; серверный код клиентские модули не вызывает.
Вызов сервера из клиентской процедуры
В управляемой форме клиентская процедура не может обратиться к базе напрямую. Типовой приём — вынести работу в серверную функцию формы или в общий модуль с флагом «Вызов сервера». Обратите внимание на директиву &НаСервереБезКонтекста: такая функция не получает данных формы и выполняется быстрее, потому что при вызове не передаётся контекст.
&НаКлиенте
Процедура ПроверитьКонтрагента(Команда)
Если ИННКорректен(Объект.ИНН) Тогда
ПоказатьПредупреждение(, "ИНН корректен");
Иначе
ПоказатьПредупреждение(, "Проверьте ИНН");
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ИННКорректен(ИНН)
Возврат РасчетыСервер.ИННКорректен(ИНН);
КонецФункции
Параметры серверной функции передаются с клиента копией, а результат возвращается значением; структуры, массивы и ссылки передаются без проблем, а вот объекты, не поддерживающие сериализацию (например, запрос или запись XML), через границу клиента и сервера передать нельзя.
Рекурсия
Подпрограмма может вызывать саму себя. Рекурсия удобна для обхода деревьев: иерархия групп справочника, вложенные каталоги, структуры XML. Обязательное условие — условие выхода, иначе вызовы будут повторяться, пока не закончится стек.
Функция Факториал(Число)
Если Число <= 1 Тогда
Возврат 1;
КонецЕсли;
Возврат Число * Факториал(Число - 1);
КонецФункции
Процедура ОбойтиКаталог(Путь, СписокФайлов)
Для Каждого Файл Из НайтиФайлы(Путь, "*") Цикл
Если Файл.ЭтоКаталог() Тогда
ОбойтиКаталог(Файл.ПолноеИмя, СписокФайлов);
Иначе
СписокФайлов.Добавить(Файл.ПолноеИмя);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Глубина вложенных вызовов ограничена. Для больших объёмов данных (например, обход иерархии тысяч элементов) лучше использовать запрос с условием В ИЕРАРХИИ, итоги по иерархии или обычный цикл с очередью. Запросы к иерархическим данным разобраны в статье Запросы в 1С.
Методы объектов и глобального контекста
Сообщить, Формат, Строка, Вычислить, ТипЗнч — это методы глобального контекста: их вызывают без указания объекта. Методы объектов вызываются через точку у конкретного экземпляра: Массив.Добавить(), Запрос.Выполнить(), Структура.Свойство(). Свои процедуры и функции модуля тоже входят в «глобальный контекст» этого модуля: внутри модуля вызываются по имени, а снаружи — через имя общего модуля или экземпляр формы/объекта, если объявлены с Экспорт.
Практические следствия: одноимённая функция модуля перекрывает глобальную, поэтому не называйте свои методы так же, как встроенные, и не давайте переменным имена методов контекста.
Выполнить() и Вычислить(): динамический код
Встроенный язык позволяет выполнить строку как код. Выполнить(Строка) выполняет оператор, а Вычислить(Выражение) возвращает значение выражения. Код выполняется в контексте текущей процедуры: локальные переменные доступны по имени.
Курс = 90;
Вычислить("Курс * 100"); // 9000
Выполнить("Сообщить(""Привет"")"); // вывод сообщения
Динамический код нужен в универсальных обработчиках и пользовательских формулах. Но он опасен: если строку вводит пользователь или она приходит из внешних данных, в ней может оказаться любой код — запись в базу, удаление файлов. Поэтому перед выполнением пользовательских строк включают безопасный режим: УстановитьБезопасныйРежим(Истина) запрещает опасные операции. В конфигурациях на БСП для этого есть готовые обёртки ОбщегоНазначения.ВычислитьВБезопасномРежиме() и ОбщегоНазначения.ВыполнитьВБезопасномРежиме(). Кроме того, динамический код не проверяется на этапе компиляции и медленнее обычного, поэтому для регулярных вычислений выбирайте обычные функции.
Типичные ошибки
- Забытый
Возвратв функции: результат оказываетсяНеопределено, и ошибка проявляется далеко от места причины. - Вызов процедуры как функции (
Результат = МояПроцедура()) приводит к ошибке компиляции или выполнения. - Неожиданное изменение переменной вызывающего кода из-за передачи по ссылке; помогает
Знач. - Значение по умолчанию, заданное не константой, например
Дата = ТекущаяДата(): язык такое не допускает, используйтеНеопределенои проверку внутри. - Вызов клиентской процедуры формы из общего серверного модуля или серверного модуля без флага «Вызов сервера» из клиента.
- Рекурсия без условия выхода или с очень большой глубиной.
- Кэширование («Повторное использование возвращаемых значений») функции, результат которой зависит от текущего пользователя или от изменяемых данных.
- Попытка вызвать не экспортную процедуру из другого модуля: метод не найден.
Актуальность для новых версий
Синтаксис процедур и функций не менялся на протяжении всей линейки 8.3. Из новых возможностей платформы стоит отметить расширение языка методами работы со строками (СтрРазделить(), СтрСоединить(), СтрШаблон()), которые используются в примерах и доступны во всех актуальных релизах 8.3. Флаги общих модулей и директивы компиляции зависят от режима работы приложения и версии платформы; в сомнительных случаях проверьте свойства модуля.
Частые вопросы
Чем процедура отличается от функции в 1С?
Функция возвращает значение через Возврат и может использоваться в выражениях, процедура значения не возвращает и вызывается как отдельная команда.
Что делает слово Экспорт?
Делает процедуру или функцию доступной из других модулей. В общих модулях без Экспорт метод виден только внутри самого модуля, в модуле формы или объекта экспортный метод становится методом формы или объекта.
Как вернуть из функции несколько значений?
Самый удобный способ — вернуть структуру с именованными полями. Другой вариант — выходной параметр: переменная, переданная без Знач, получает новое значение внутри подпрограммы.
Безопасно ли использовать Выполнить() и Вычислить()?
Только с проверенными строками. Если выражение приходит от пользователя или из внешних данных, перед вызовом включите безопасный режим или используйте обёртки БСП вроде ОбщегоНазначения.ВычислитьВБезопасномРежиме().