К статье
Практический материал

Разработка и Конфигурация · 13 мин

Чем отличается функция от процедуры в 1С

Разбираем, чем функция отличается от процедуры в 1С: возврат значения, синтаксис, экспорт, типичные ошибки и примеры кода на встроенном языке.

Функция и процедура — два базовых способа объединить код в именованный блок во встроенном языке 1С. Главное различие между ними одно: функция обязана вернуть значение через оператор Возврат, а процедура ничего не возвращает и используется только для выполнения действий. Из этого единственного отличия вытекают все остальные: где можно вызывать код, как он участвует в выражениях и как компилятор проверяет правильность программы.

На практике выбор между ними определяет читаемость и надёжность конфигурации. Если блок кода вычисляет что-то — сумму скидки, статус документа, признак доступности — это функция. Если блок выполняет действие — записывает объект, выводит сообщение, заполняет табличную часть — это процедура. Смешение ролей приводит к ошибкам, которые сложно отловить: например, к «функциям», которые молча меняют данные, или к процедурам, результат которых приходится вытаскивать через параметры.

Ниже разобраны синтаксис обеих конструкций, правила вызова, работа с параметрами, типичные ошибки новичков и критерии выбора — с примерами кода, которые можно сразу проверить в конфигураторе.

Синтаксис: как объявляются функция и процедура

Обе конструкции объявляются в модуле и имеют похожую структуру: заголовок с именем и параметрами, тело и ключевое слово завершения. Различаются ключевые слова начала и конца, а также наличие возврата значения.

Функция РассчитатьСкидку(СуммаДокумента, ПроцентСкидки) Экспорт

Скидка = СуммаДокумента * ПроцентСкидки / 100;

Возврат Скидка;

КонецФункции

Процедура ЗаполнитьТабличнуюЧасть(ДокументОбъект) Экспорт

ДокументОбъект.Товары.Очистить();

// дальнейшая логика заполнения

КонецПроцедуры

Ключевое слово Экспорт в обоих случаях делает метод доступным из других модулей — например, из модуля объекта при обращении к общему модулю. Без Экспорт функция или процедура видна только внутри своего модуля. Это одинаково для обеих конструкций.

Обратите внимание: у функции тело обязано содержать хотя бы один исполняемый Возврат со значением. Если написать функцию без возврата, конфигуратор при проверке модуля выдаст предупреждение, а при выполнении такой функции без достигнутого Возврат результат будет неопределённым — это частый источник трудноуловимых ошибок.

Возврат значения — главное различие

Функция возвращает ровно одно значение любого типа: число, строку, дату, булево, ссылку, структуру, массив. Оператор Возврат Значение; немедленно завершает выполнение функции и передаёт результат в точку вызова. Если нужно вернуть несколько результатов, их упаковывают в структуру:

Функция ПолучитьДанныеКонтрагента(КонтрагентСсылка) Экспорт

Результат = Новый Структура;

Результат.Вставить("Наименование", КонтрагентСсылка.Наименование);

Результат.Вставить("ИНН", КонтрагентСсылка.ИНН);

Возврат Результат;

КонецФункции

Процедура тоже может содержать оператор Возврат;, но только без значения — он используется для досрочного выхода из тела процедуры, например при невыполненном условии. Вызов процедуры нельзя подставить в выражение: запись Х = МояПроцедура(); является синтаксической ошибкой, и модуль не пройдёт проверку.

Как вызываются функции и процедуры

Функция вызывается внутри выражения: справа от знака присваивания, в условии Если, в качестве аргумента другой функции или в составе арифметической операции. Это её ключевое преимущество — результат сразу участвует в вычислениях:

Скидка = РассчитатьСкидку(СуммаДокумента, 10);

Если РассчитатьСкидку(СуммаДокумента, 10) > 1000 Тогда

Сообщить("Применена повышенная скидка");

КонецЕсли;

Процедура вызывается отдельным оператором — её имя с параметрами пишется как самостоятельная строка кода. Попытка использовать процедуру в выражении приведёт к ошибке компиляции модуля ещё до запуска:

ЗаполнитьТабличнуюЧасть(Объект);

СообщитьПользователю("Заполнение завершено");

Вызов функции тоже можно записать как отдельный оператор, проигнорировав возвращаемое значение. Технически это сработает, но считается плохим стилем: если результат не нужен, логичнее было оформить код процедурой. Осознанно так делают редко — например, когда важен побочный эффект, а результат проверять не требуется.

Параметры: Знач и передача по ссылке

И функции, и процедуры принимают параметры одинаково. По умолчанию параметры передаются по ссылке: изменения параметра внутри метода видны снаружи. Ключевое слово Знач перед именем параметра включает передачу по значению — внутри метода работает копия, и исходная переменная не меняется.

Процедура УвеличитьСумму(Сумма) // по ссылке

Сумма = Сумма + 100;

КонецПроцедуры

Процедура УвеличитьСуммуБезопасно(Знач Сумма) // по значению

Сумма = Сумма + 100; // исходная переменная не изменится

КонецПроцедуры

Здесь кроется важное следствие: процедура формально «ничего не возвращает», но фактически может передать результат наружу через параметры, переданные по ссылке. Это законный приём, но злоупотреблять им не стоит — код с пятью «выходными» параметрами читается хуже, чем функция, возвращающая структуру.

Функция или процедура

Функция

Код вычисляет результат: сумму, признак, найденную ссылку, текст. Результат нужен вызывающему коду для дальнейших вычислений или проверок.

Процедура

Код выполняет действие: записывает объект, заполняет табличную часть, выводит сообщение, устанавливает параметры. Возвращать нечего или результат очевиден из самого действия.

Функция со структурой

Нужно вернуть несколько значений. Вместо набора параметров по ссылке функция возвращает структуру с именованными полями — так код понятнее.

Контекст выполнения и директивы компиляции

В модулях форм, команд и объектов перед объявлением функции или процедуры указывается директива компиляции: &НаКлиенте, &НаСервере, &НаСервереБезКонтекста и другие. Директивы одинаково применимы к обоим типам методов, но накладывают ограничения на вызовы.

Клиентская процедура может вызвать серверную функцию, если та объявлена с &НаСервере или &НаСервереБезКонтекста и помечена Экспорт в общем серверном модуле с соответствующими флагами. Обратный вызов — с сервера на клиента — невозможен: серверный код не имеет доступа к клиентскому контексту. Это ограничение платформы, а не различие между функцией и процедурой.

Дополнительно функции в общих модулях с признаком «Сервер» часто используют для получения данных с клиента: клиентская процедура обработчика нажатия кнопки вызывает серверную функцию, получает результат и отображает его. Такая связка — типовой шаблон в управляемых формах:

&НаКлиенте

Процедура РассчитатьНажатие(Команда)

Скидка = РассчитатьСкидкуНаСервере(Объект.СуммаДокумента);

Объект.Скидка = Скидка;

КонецПроцедуры

&НаСервереБезКонтекста

Функция РассчитатьСкидкуНаСервере(Сумма)

Возврат Сумма * 0.1;

КонецФункции

Типичные ошибки и как их избежать

Первая и самая частая ошибка — функция, в которой не все ветки логики заканчиваются оператором Возврат. Например, Возврат стоит внутри Если, а для ветки Иначе забыт. На этапе проверки модуля конфигуратор может это не заблокировать, а в runtime функция вернёт Неопределено, что обрушит дальнейшие вычисления. Правило простое: последняя исполняемая строка функции — всегда Возврат со значением по умолчанию.

Вторая ошибка — побочные эффекты в функциях. Если функция ПолучитьОстаток() вдобавок записывает регистр или изменяет документ, вызывающий код становится непредсказуемым: функцию могут вызвать дважды в одном выражении или в цикле, и каждый вызов изменит данные. Функции по возможности должны только читать и вычислять; запись — задача процедур.

Третья группа ошибок связана с областью видимости: попытка вызвать неэкспортный метод из другого модуля, обращение к клиентскому методу с сервера, использование переменных модуля, не инициализированных в текущем контексте. Все они диагностируются при проверке модуля (Конфигуратор → пункт проверки модуля в меню «Текст»), поэтому запускайте проверку после каждого изменения, а не перед самым обновлением базы.

Типичные ошибки
01
Функция без Возврат в одной из веток — на выходе Неопределено и падение вычислений ниже по коду.
02
Попытка присвоить результат процедуры: Х = МояПроцедура() — модуль не пройдёт синтаксическую проверку.
03
Изменение глобальных данных внутри функции — повторный вызов функции неожиданно меняет состояние системы.
04
Передача результата через множество параметров по ссылке вместо возврата структуры из функции — код трудно читать и тестировать.
05
Вызов серверной функции из клиентского контекста без признака Сервер у общего модуля — ошибка вызова в тонком клиенте.

Как выбрать: практические критерии

Задайте себе один вопрос: «Вызывающий код ждёт от этого блока ответ?» Если да — функция. Если блок просто что-то делает и успех действия очевиден (или проверяется исключениями) — процедура. Этот критерий покрывает большинство ситуаций.

Именование помогает удерживать дисциплину. Функции называйте так, чтобы имя отвечало на вопрос «что вернётся»: ПолучитьОстаток, РассчитатьНДС, ЭтоПроведённыйДокумент (для булевых функций удобен формат вопроса). Процедуры называйте глаголом действия: ЗаписатьДокумент, ЗаполнитьПоОснованию, УстановитьВидимостьЭлементов. Если имя функции звучит как действие, а имя процедуры — как результат, это сигнал пересмотреть тип метода.

Маршрут решения

Как выбрать действие

Код вычисляет значение, которое нужно дальше в выражении или условииОбъявите функцию и верните результат через Возврат
Код только меняет состояние: записывает, заполняет, выводит сообщениеОбъявите процедуру без возврата значения
Нужно вернуть два и более результатаФункция, возвращающая структуру с именованными полями
Результат — признак успеха выполнения действияФункция с возвратом Булево (Истина — успешно, Ложь — ошибка)

Проверка результата после написания кода

После написания метода выполните синтаксическую проверку модуля: в конфигураторе откройте модуль и используйте пункт «Проверка модуля» в меню «Текст». Конфигуратор покажет ошибки вызовов процедур в выражениях, обращения к несуществующим методам и проблемы видимости. Для функций дополнительно проследите глазами, что каждая ветка завершения содержит Возврат.

Затем проверьте поведение в отладчике: поставьте точку останова на вызове, шагните внутрь метода (F11) и посмотрите значение, которое уходит в Возврат. Для функции убедитесь, что тип результата совпадает с ожиданиями вызывающего кода — например, что вместо строки не возвращается число, а вместо ссылки — Неопределено.

Контрольные точки

Как проверить результат

Проверка модуля в конфигураторе
Норма

Ошибок и предупреждений нет

Тревожный признак

Сообщения о несоответствии типов, недоступных методах, отсутствии Возврат

Возвращаемое значение функции в отладчике
Норма

Ожидаемый тип и осмысленное значение на всех ветках

Тревожный признак

Неопределено или значение неожиданного типа

Побочные эффекты функции
Норма

Функция только читает данные и вычисляет

Тревожный признак

После вызова функции изменились записи в базе

FAQ: частые вопросы о функциях и процедурах в 1С

Может ли процедура вернуть значение через параметр?

Да. Если параметр объявлен без ключевого слова Знач, он передаётся по ссылке, и присвоение ему нового значения внутри процедуры будет видно в вызывающем коде. Это рабочий приём, но при нескольких выходных значениях удобнее вернуть структуру из функции — так сигнатура метода остаётся читаемой.

Что вернёт функция, если в ней нет оператора Возврат?

Функция вернёт значение Неопределено. Конфигуратор обычно предупреждает о такой ситуации при проверке модуля, но выполнение не блокируется. Неопределено в последующих арифметических операциях вызовет ошибку преобразования типов, поэтому всегда завершайте функцию явным Возврат со значением по умолчанию.

Можно ли вызвать функцию и проигнорировать её результат?

Технически да: вызов функции отдельным оператором допустим, и возвращаемое значение просто отбрасывается. Однако это считается плохим стилем — если результат не нужен, логичнее оформить код процедурой. Осознанно так делают, когда важен побочный эффект вызова.

Чем отличается Возврат в процедуре от Возврата в функции?

В процедуре Возврат пишется без значения и означает только досрочный выход из тела метода. В функции Возврат обязан содержать значение — оно передаётся в точку вызова как результат. Возврат без значения в функции вернёт Неопределено, что почти всегда является ошибкой.

Можно ли из клиентской процедуры вызвать серверную функцию?

Да, если функция объявлена с директивой &НаСервере или &НаСервереБезКонтекста, помечена Экспорт и находится в общем модуле с установленным признаком «Сервер» (и «Вызов сервера» для клиентских вызовов). Обратный вызов — с сервера на клиента — платформой не поддерживается.

Когда стоит предпочесть функцию с возвратом Булево вместо процедуры?

Когда действие может завершиться неудачей и вызывающий код должен на это отреагировать: например, функция ЗаписатьДокумент() возвращает Истина при успехе и Ложь при ошибке, а вызывающий код показывает пользователю сообщение. Если неуспех невозможен или обрабатывается исключением, достаточно процедуры.

Итог

Что важно запомнить

Функция возвращает значение через Возврат и используется в выражениях; процедура выполняет действие и вызывается отдельным оператором. Выбирайте тип метода по ответу на вопрос «нужен ли результат вызывающему коду», завершайте функции Возврат на всех ветках и не допускайте побочных эффектов в функциях.