Разработка и Конфигурация · 13 мин
Чем отличается функция от процедуры в 1С
Разбираем, чем функция отличается от процедуры в 1С: возврат значения, синтаксис, экспорт, типичные ошибки и примеры кода на встроенном языке.
Функция и процедура — два базовых способа объединить код в именованный блок во встроенном языке 1С. Главное различие между ними одно: функция обязана вернуть значение через оператор Возврат, а процедура ничего не возвращает и используется только для выполнения действий. Из этого единственного отличия вытекают все остальные: где можно вызывать код, как он участвует в выражениях и как компилятор проверяет правильность программы.
На практике выбор между ними определяет читаемость и надёжность конфигурации. Если блок кода вычисляет что-то — сумму скидки, статус документа, признак доступности — это функция. Если блок выполняет действие — записывает объект, выводит сообщение, заполняет табличную часть — это процедура. Смешение ролей приводит к ошибкам, которые сложно отловить: например, к «функциям», которые молча меняют данные, или к процедурам, результат которых приходится вытаскивать через параметры.
Ниже разобраны синтаксис обеих конструкций, правила вызова, работа с параметрами, типичные ошибки новичков и критерии выбора — с примерами кода, которые можно сразу проверить в конфигураторе.
Синтаксис: как объявляются функция и процедура
Обе конструкции объявляются в модуле и имеют похожую структуру: заголовок с именем и параметрами, тело и ключевое слово завершения. Различаются ключевые слова начала и конца, а также наличие возврата значения.
Функция РассчитатьСкидку(СуммаДокумента, ПроцентСкидки) Экспорт
Скидка = СуммаДокумента * ПроцентСкидки / 100;
Возврат Скидка;
КонецФункции
Процедура ЗаполнитьТабличнуюЧасть(ДокументОбъект) Экспорт
ДокументОбъект.Товары.Очистить();
// дальнейшая логика заполнения
КонецПроцедуры
Ключевое слово Экспорт в обоих случаях делает метод доступным из других модулей — например, из модуля объекта при обращении к общему модулю. Без Экспорт функция или процедура видна только внутри своего модуля. Это одинаково для обеих конструкций.
Обратите внимание: у функции тело обязано содержать хотя бы один исполняемый Возврат со значением. Если написать функцию без возврата, конфигуратор при проверке модуля выдаст предупреждение, а при выполнении такой функции без достигнутого Возврат результат будет неопределённым — это частый источник трудноуловимых ошибок.
Возврат значения — главное различие
Функция возвращает ровно одно значение любого типа: число, строку, дату, булево, ссылку, структуру, массив. Оператор Возврат Значение; немедленно завершает выполнение функции и передаёт результат в точку вызова. Если нужно вернуть несколько результатов, их упаковывают в структуру:
Функция ПолучитьДанныеКонтрагента(КонтрагентСсылка) Экспорт
Результат = Новый Структура;
Результат.Вставить("Наименование", КонтрагентСсылка.Наименование);
Результат.Вставить("ИНН", КонтрагентСсылка.ИНН);
Возврат Результат;
КонецФункции
Процедура тоже может содержать оператор Возврат;, но только без значения — он используется для досрочного выхода из тела процедуры, например при невыполненном условии. Вызов процедуры нельзя подставить в выражение: запись Х = МояПроцедура(); является синтаксической ошибкой, и модуль не пройдёт проверку.
Как вызываются функции и процедуры
Функция вызывается внутри выражения: справа от знака присваивания, в условии Если, в качестве аргумента другой функции или в составе арифметической операции. Это её ключевое преимущество — результат сразу участвует в вычислениях:
Скидка = РассчитатьСкидку(СуммаДокумента, 10);
Если РассчитатьСкидку(СуммаДокумента, 10) > 1000 Тогда
Сообщить("Применена повышенная скидка");
КонецЕсли;
Процедура вызывается отдельным оператором — её имя с параметрами пишется как самостоятельная строка кода. Попытка использовать процедуру в выражении приведёт к ошибке компиляции модуля ещё до запуска:
ЗаполнитьТабличнуюЧасть(Объект);
СообщитьПользователю("Заполнение завершено");
Вызов функции тоже можно записать как отдельный оператор, проигнорировав возвращаемое значение. Технически это сработает, но считается плохим стилем: если результат не нужен, логичнее было оформить код процедурой. Осознанно так делают редко — например, когда важен побочный эффект, а результат проверять не требуется.
Параметры: Знач и передача по ссылке
И функции, и процедуры принимают параметры одинаково. По умолчанию параметры передаются по ссылке: изменения параметра внутри метода видны снаружи. Ключевое слово Знач перед именем параметра включает передачу по значению — внутри метода работает копия, и исходная переменная не меняется.
Процедура УвеличитьСумму(Сумма) // по ссылке
Сумма = Сумма + 100;
КонецПроцедуры
Процедура УвеличитьСуммуБезопасно(Знач Сумма) // по значению
Сумма = Сумма + 100; // исходная переменная не изменится
КонецПроцедуры
Здесь кроется важное следствие: процедура формально «ничего не возвращает», но фактически может передать результат наружу через параметры, переданные по ссылке. Это законный приём, но злоупотреблять им не стоит — код с пятью «выходными» параметрами читается хуже, чем функция, возвращающая структуру.
Функция или процедура
Код вычисляет результат: сумму, признак, найденную ссылку, текст. Результат нужен вызывающему коду для дальнейших вычислений или проверок.
Код выполняет действие: записывает объект, заполняет табличную часть, выводит сообщение, устанавливает параметры. Возвращать нечего или результат очевиден из самого действия.
Нужно вернуть несколько значений. Вместо набора параметров по ссылке функция возвращает структуру с именованными полями — так код понятнее.
Контекст выполнения и директивы компиляции
В модулях форм, команд и объектов перед объявлением функции или процедуры указывается директива компиляции: &НаКлиенте, &НаСервере, &НаСервереБезКонтекста и другие. Директивы одинаково применимы к обоим типам методов, но накладывают ограничения на вызовы.
Клиентская процедура может вызвать серверную функцию, если та объявлена с &НаСервере или &НаСервереБезКонтекста и помечена Экспорт в общем серверном модуле с соответствующими флагами. Обратный вызов — с сервера на клиента — невозможен: серверный код не имеет доступа к клиентскому контексту. Это ограничение платформы, а не различие между функцией и процедурой.
Дополнительно функции в общих модулях с признаком «Сервер» часто используют для получения данных с клиента: клиентская процедура обработчика нажатия кнопки вызывает серверную функцию, получает результат и отображает его. Такая связка — типовой шаблон в управляемых формах:
&НаКлиенте
Процедура РассчитатьНажатие(Команда)
Скидка = РассчитатьСкидкуНаСервере(Объект.СуммаДокумента);
Объект.Скидка = Скидка;
КонецПроцедуры
&НаСервереБезКонтекста
Функция РассчитатьСкидкуНаСервере(Сумма)
Возврат Сумма * 0.1;
КонецФункции
Типичные ошибки и как их избежать
Первая и самая частая ошибка — функция, в которой не все ветки логики заканчиваются оператором Возврат. Например, Возврат стоит внутри Если, а для ветки Иначе забыт. На этапе проверки модуля конфигуратор может это не заблокировать, а в runtime функция вернёт Неопределено, что обрушит дальнейшие вычисления. Правило простое: последняя исполняемая строка функции — всегда Возврат со значением по умолчанию.
Вторая ошибка — побочные эффекты в функциях. Если функция ПолучитьОстаток() вдобавок записывает регистр или изменяет документ, вызывающий код становится непредсказуемым: функцию могут вызвать дважды в одном выражении или в цикле, и каждый вызов изменит данные. Функции по возможности должны только читать и вычислять; запись — задача процедур.
Третья группа ошибок связана с областью видимости: попытка вызвать неэкспортный метод из другого модуля, обращение к клиентскому методу с сервера, использование переменных модуля, не инициализированных в текущем контексте. Все они диагностируются при проверке модуля (Конфигуратор → пункт проверки модуля в меню «Текст»), поэтому запускайте проверку после каждого изменения, а не перед самым обновлением базы.
Как выбрать: практические критерии
Задайте себе один вопрос: «Вызывающий код ждёт от этого блока ответ?» Если да — функция. Если блок просто что-то делает и успех действия очевиден (или проверяется исключениями) — процедура. Этот критерий покрывает большинство ситуаций.
Именование помогает удерживать дисциплину. Функции называйте так, чтобы имя отвечало на вопрос «что вернётся»: ПолучитьОстаток, РассчитатьНДС, ЭтоПроведённыйДокумент (для булевых функций удобен формат вопроса). Процедуры называйте глаголом действия: ЗаписатьДокумент, ЗаполнитьПоОснованию, УстановитьВидимостьЭлементов. Если имя функции звучит как действие, а имя процедуры — как результат, это сигнал пересмотреть тип метода.
Как выбрать действие
Проверка результата после написания кода
После написания метода выполните синтаксическую проверку модуля: в конфигураторе откройте модуль и используйте пункт «Проверка модуля» в меню «Текст». Конфигуратор покажет ошибки вызовов процедур в выражениях, обращения к несуществующим методам и проблемы видимости. Для функций дополнительно проследите глазами, что каждая ветка завершения содержит Возврат.
Затем проверьте поведение в отладчике: поставьте точку останова на вызове, шагните внутрь метода (F11) и посмотрите значение, которое уходит в Возврат. Для функции убедитесь, что тип результата совпадает с ожиданиями вызывающего кода — например, что вместо строки не возвращается число, а вместо ссылки — Неопределено.
Как проверить результат
Ошибок и предупреждений нет
Сообщения о несоответствии типов, недоступных методах, отсутствии Возврат
Ожидаемый тип и осмысленное значение на всех ветках
Неопределено или значение неожиданного типа
Функция только читает данные и вычисляет
После вызова функции изменились записи в базе
FAQ: частые вопросы о функциях и процедурах в 1С
Может ли процедура вернуть значение через параметр?
Да. Если параметр объявлен без ключевого слова Знач, он передаётся по ссылке, и присвоение ему нового значения внутри процедуры будет видно в вызывающем коде. Это рабочий приём, но при нескольких выходных значениях удобнее вернуть структуру из функции — так сигнатура метода остаётся читаемой.
Что вернёт функция, если в ней нет оператора Возврат?
Функция вернёт значение Неопределено. Конфигуратор обычно предупреждает о такой ситуации при проверке модуля, но выполнение не блокируется. Неопределено в последующих арифметических операциях вызовет ошибку преобразования типов, поэтому всегда завершайте функцию явным Возврат со значением по умолчанию.
Можно ли вызвать функцию и проигнорировать её результат?
Технически да: вызов функции отдельным оператором допустим, и возвращаемое значение просто отбрасывается. Однако это считается плохим стилем — если результат не нужен, логичнее оформить код процедурой. Осознанно так делают, когда важен побочный эффект вызова.
Чем отличается Возврат в процедуре от Возврата в функции?
В процедуре Возврат пишется без значения и означает только досрочный выход из тела метода. В функции Возврат обязан содержать значение — оно передаётся в точку вызова как результат. Возврат без значения в функции вернёт Неопределено, что почти всегда является ошибкой.
Можно ли из клиентской процедуры вызвать серверную функцию?
Да, если функция объявлена с директивой &НаСервере или &НаСервереБезКонтекста, помечена Экспорт и находится в общем модуле с установленным признаком «Сервер» (и «Вызов сервера» для клиентских вызовов). Обратный вызов — с сервера на клиента — платформой не поддерживается.
Когда стоит предпочесть функцию с возвратом Булево вместо процедуры?
Когда действие может завершиться неудачей и вызывающий код должен на это отреагировать: например, функция ЗаписатьДокумент() возвращает Истина при успехе и Ложь при ошибке, а вызывающий код показывает пользователю сообщение. Если неуспех невозможен или обрабатывается исключением, достаточно процедуры.
Что важно запомнить
Функция возвращает значение через Возврат и используется в выражениях; процедура выполняет действие и вызывается отдельным оператором. Выбирайте тип метода по ответу на вопрос «нужен ли результат вызывающему коду», завершайте функции Возврат на всех ветках и не допускайте побочных эффектов в функциях.