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