Разработка и Конфигурация · 11 мин
В чем разница между процедурой и функцией в 1С
Разбираем разницу между процедурой и функцией в 1С: возврат значений, контекст вызова, сервер и клиент, типичные ошибки и правила выбора.
Процедура и функция — два базовых способа оформить повторно используемый код во встроенном языке 1С. Синтаксически они почти идентичны: обе объявляются в модуле, принимают параметры и содержат тело с операторами. Разница заключается в одном ключевом свойстве: функция возвращает значение через оператор Возврат, а процедура — нет. Из этого единственного отличия вытекают все правила их применения.
От выбора между процедурой и функцией зависит не только стиль кода, но и его работоспособность. Функцию можно вызывать внутри выражений и использовать её результат в присваиваниях, условиях и запросах к другим методам. Процедура вызывается только отдельным оператором и не может участвовать в выражении. Попытка присвоить результат процедуры переменной приведёт к ошибке компиляции модуля.
Дополнительные ограничения накладывает контекст исполнения: клиент и сервер, тонкий клиент и веб-клиент, наличие разных видов директив компиляции. Неправильно выбранный тип метода в связке с контекстом — частая причина ошибок «Процедура или функция с указанным именем не определена» и лишних серверных вызовов, замедляющих работу формы.
Синтаксис объявления: как отличить метод в коде
Объявление функции начинается с ключевого слова Функция и завершается КонецФункции. Внутри тела обязателен хотя бы один оператор Возврат с выражением, иначе функция вернёт Неопределено, что часто маскирует ошибку. Объявление процедуры начинается со слова Процедура и заканчивается КонецПроцедуры; оператор Возврат в ней допустим, но только без значения — как способ досрочного выхода.
Оба метода могут иметь параметры, в том числе со значениями по умолчанию, и ключевое слово Экспорт для доступа из других модулей. Перед объявлением указывается директива компиляции: &НаКлиенте, &НаСервере, &НаСервереБезКонтекста и другие. Директива определяет, где будет исполняться код, и напрямую влияет на то, какие объекты внутри метода доступны.
&НаСервере
Функция СуммаСНДС(Сумма, Ставка) Экспорт
Возврат Сумма + Сумма * Ставка / 100;
КонецФункции
&НаСервере
Процедура ЗаписатьЛог(Текст) Экспорт
ЗаписьЖурналаРегистрации(Текст);
Возврат; // досрочный выход без значения
КонецПроцедурыВизуально в конфигураторе функции и процедуры отображаются в дереве модуля без различий по значкам, поэтому при чтении чужого кода ориентируйтесь на первое ключевое слово объявления и наличие Возврат с выражением. Быстрый способ понять тип метода — поставить курсор на вызов и нажать F12: конфигуратор перейдёт к объявлению.
Главное отличие: возвращаемое значение
Функция обязана вернуть значение на каждом пути выполнения. Если хотя бы одна ветка условий завершается без Возврат, функция вернёт Неопределено, и вызывающий код может получить ошибку сравнения типов или неочевидное поведение. Поэтому при рефакторинге функции проверяйте все ветки Если, Попытка и циклы с досрочным выходом.
Процедура выполняет действие: записывает данные, открывает форму, выводит сообщение, меняет реквизиты объекта. Её нельзя подставить в правую часть присваивания. Конструкция Результат = МояПроцедура(); не скомпилируется, потому что процедура не является выражением. Это принципиальное ограничение языка, а не вопрос настройки.
Обходной путь, когда «результат» всё же нужен, — передача параметра по значению с его изменением внутри процедуры (параметры без Знач передаются по ссылке и могут быть изменены). Однако такой приём ухудшает читаемость: корректнее превратить метод в функцию, если его назначение — вернуть данные.
Процедура или функция
Нужен результат вычисления, проверка условия, значение для выражения. Можно вызывать внутри формул и условий.
Нужно выполнить действие без результата: запись, вывод, открытие формы. Вызывается только отдельным оператором.
Как выбрать: практические критерии
Простое правило: если вызов метода естественно читается как вопрос «что получится?» — это функция. Если как команда «сделай это» — это процедура. ПолучитьОстаток(Номенклатура), ЭтоПолноправныйПользователь(), РассчитатьСкидку(Документ) — функции. УстановитьВидимостьЭлементов(), ПровестиДокумент(Ссылка), ОповеститьПользователей() — процедуры.
Функции, возвращающие булево, удобны в условиях: Если ДокументЗаполненКорректно(Объект) Тогда .... Такой код невозможен с процедурой. Наоборот, обработчики событий формы (ПриОткрытии, ПередЗаписью) по соглашениям платформы являются процедурами, потому что платформа не ожидает от них возвращаемого значения — результат передаётся через параметры события, например Отказ.
Отдельный случай — методы, которые что-то вычисляют и одновременно меняют состояние. Такие лучше разделить: функция возвращает значение без побочных эффектов, процедура выполняет изменение. Смешение ролей затрудняет тестирование и приводит к неожиданным повторным вычислениям, когда функцию вызывают ради результата, а она заодно перезаписывает данные.
Контекст исполнения: клиент и сервер
В управляемых формах методы делятся по директивам компиляции. Клиентские процедуры и функции (&НаКлиенте) работают с интерфейсом, но не имеют прямого доступа к базе данных. Серверные (&НаСервере, &НаСервереБезКонтекста) могут выполнять запросы и обращаться к данным, но не видят элементы формы напрямую в варианте «без контекста».
Здесь разница между процедурой и функцией проявляется в производительности. Серверная функция, вызванная из клиентского кода внутри цикла, порождает серверный вызов на каждой итерации — форма начинает заметно «подтормаживать». Правильный приём — собрать данные на клиенте в массив или структуру и один раз вызвать серверную функцию, которая вернёт все нужные значения пакетом.
Ещё одно ограничение: асинхронная модель. В тонком и веб-клиенте многие интерактивные операции (вопрос пользователю, выбор файла) выполняются асинхронно через описания оповещений, и обработчики этих оповещений оформляются процедурами. Функция не может «подождать» ответ пользователя синхронно в режиме, где модальные вызовы запрещены, — это нужно учитывать при проектировании логики.
Экспорт и вызов из других модулей
Ключевое слово Экспорт делает процедуру или функцию доступной извне: из общих модулей, модулей объектов, форм. Без него метод виден только внутри своего модуля. Типичная архитектура — общие серверные модули с экспортными функциями прикладной логики и тонкие клиентские обработчики, которые эти функции вызывают.
Обращение к экспортному методу общего модуля идёт через имя модуля: РаботаСЦенами.ПолучитьЦену(Номенклатура, Дата). Если метод объявлен процедурой, а вы пытаетесь использовать его в выражении, компилятор сообщит об ошибке ещё до запуска — это одна из самых частых причин доработки чужих модулей при интеграции.
При изменении сигнатуры экспортной функции (добавлении параметра, смене типа возвращаемого значения) проверьте все места вызова через поиск по конфигурации. Процедуры в этом смысле чуть проще: раз они не участвуют в выражениях, их вызовы локализованы отдельными строками и легче находятся.
- 1Сформулируйте назначение метода одним предложением: вычислить значение или выполнить действие
- 2Если нужен результат — объявите Функция и убедитесь, что Возврат есть на всех ветках выполнения
- 3Проверьте контекст: директива компиляции соответствует тому, где метод будет вызываться
- 4Скомпилируйте модуль и проверьте все места вызова через поиск ссылок
- 5Запустите сценарий в тестовой базе и убедитесь, что возвращаемое значение имеет ожидаемый тип
Типичные ошибки и как их избежать
Самая распространённая ошибка — функция без Возврат на одной из веток. Код компилируется, но в редком сценарии возвращает Неопределено, и ошибка всплывает далеко от источника. Вторая частая проблема — попытка использовать процедуру в выражении: Если ЗаписатьДокумент(Ссылка) Тогда не скомпилируется, и новички тратят время на поиск причины вместо смены типа метода.
Третья группа ошибок связана с контекстом: серверная функция вызывается из клиентского модуля без права вызова сервера, либо клиентская процедура обращается к менеджеру объекта, недоступному на клиенте. Признаки — сообщения о недоступности метода или переменной. Лечится переносом логики в общий модуль с корректными флагами «Сервер», «Вызов сервера», «Клиент».
Наконец, избыточное использование функций ради «универсальности» вредит: функция, которая внутри записывает данные и возвращает признак успеха, неожиданно срабатывает при повторных вызовах в отладчике или при вычислении в условиях. Действия с побочными эффектами оформляйте процедурами, а проверку успеха делайте отдельной функцией или через параметр-структуру результата.
Проверка результата и отладка
Убедиться, что метод работает как задумано, проще всего через отладчик конфигуратора: точка останова на строке вызова, затем шаг с заходом (F11) внутрь метода. Для функции проверьте значение на каждом Возврат через табло (Shift+F9) — так видно, какая ветка сработала. Для процедуры контролируйте изменение параметров и состояния объектов после вызова.
Быстрая проверка типа возвращаемого значения — функция ТипЗнч() в сочетании с выводом в сообщение или журнал. Если функция должна вернуть число, а приходит строка, причина обычно в ветке, где результат не рассчитан. Для процедур полезен контроль «до/после»: зафиксируйте состояние объекта до вызова и сравните после.
Как проверить результат
На каждой ветке Возврат отдаёт значение ожидаемого типа
В отладчике видно Неопределено или тип не соответствует ожиданию
Поиск по конфигурации показывает все вызовы, все компилируются
После изменения сигнатуры часть модулей выдаёт ошибку компиляции
Серверный вызов один на действие пользователя
В замере производительности видны повторяющиеся серверные вызовы в цикле
Частые вопросы
Может ли процедура вернуть значение через параметр?
Да. Параметры, объявленные без ключевого слова «Знач», передаются по ссылке, и процедура может их изменить. Но если назначение метода — именно получить результат, корректнее оформить его функцией: так код читается однозначно.
Что вернёт функция, если в ней нет оператора Возврат?
Функция вернёт значение Неопределено. Компилятор это не считает ошибкой, поэтому дефект проявляется только в момент выполнения — часто как ошибка сравнения типов в вызывающем коде.
Чем отличается обработчик события формы от обычной процедуры?
Обработчик события — это процедура, которую платформа вызывает автоматически при наступлении события (ПриОткрытии, ПередЗаписью и т.п.). Результат передаётся не возвратом, а через параметры события, например Отказ или СтандартнаяОбработка.
Можно ли вызвать серверную функцию из клиентского кода?
Да, если функция объявлена с директивой &НаСервере или находится в общем модуле с установленным флагом «Вызов сервера». Учитывайте, что каждый такой вызов — обращение к серверу, поэтому в циклах их следует избегать.
Что выбрать для метода, который и вычисляет, и записывает данные?
Лучше разделить на два метода: функцию для вычисления значения без побочных эффектов и процедуру для записи. Если разделение невозможно, оформите метод процедурой, а результат передавайте через параметр-структуру — так поведение останется предсказуемым при повторных вызовах.
Что важно запомнить
Функция возвращает значение и работает внутри выражений, процедура выполняет действие и вызывается отдельным оператором. Выбирайте по назначению метода, следите за Возврат на всех ветках функции и не вызывайте серверные функции из клиентских циклов.