Функции, Возврат и результат вызова во встроенном языке 1С
Содержание
Возвращаемое значение в 1С — это результат, который функция отдаёт в место вызова через оператор Возврат. Написали Сумма = ПосчитатьИтог(Документ) — в переменную Сумма попадёт то, что стоит после Возврат внутри функции. Если функция дошла до КонецФункции, так и не встретив Возврат, она вернёт Неопределено.
Возвращать значение умеют только функции. Процедура выполняет действия и ничего не отдаёт. Встроенный язык 1С:Предприятия 8.3 устроен здесь одинаково для всех конфигураций: правила ниже работают и в БП 3.0, и в ЗУП 3.1, и в самописной базе.
Как устроен возврат значения: синтаксис и правила
Минимальная функция выглядит так:
Функция Удвоить(Число)
Возврат Число * 2;
КонецФункции
// вызов
Результат = Удвоить(21); // Результат = 42
Что происходит при вызове:
- Платформа передаёт в функцию параметр
Числосо значением 21. - Выполняется выражение после
Возврат— получается 42. - Работа функции сразу прекращается, код ниже
Возвратне выполняется. - Значение 42 подставляется в выражение на месте вызова.
Ключевые правила, которые стоит держать в голове:
- 🔹
Возвратзавершает функцию немедленно. Всё, что ниже, в этой ветке не выполнится. - 🔹
Возвратможет стоять в нескольких местах. Обычно это проверки в начале функции и основной результат в конце. - 🔹 Нет
Возврат— результатНеопределено. Ошибки при этом не будет, что и делает ситуацию опасной. - 🔹 Тип результата не объявляется. Одна и та же функция может вернуть число в одной ветке и строку в другой. Язык это позволяет, но читать такой код тяжело.
- 🔹 Вызов можно встроить в выражение:
Если ЕстьОстаток(Товар) Тогда,Итог = Цена(Товар) * Количество.
Функцию с ключевым словом Экспорт можно вызывать из других модулей: ОбщегоНазначения.ИмяФункции() в общем модуле, Документы.Реализация.ИмяФункции() в модуле менеджера.
Функция или процедура: в чём разница для результата
Синтаксически они похожи, но ведут себя по-разному.
| Признак | Функция | Процедура |
|---|---|---|
| Ключевые слова | Функция … КонецФункции | Процедура … КонецПроцедуры |
| Возвращает значение | Да, через Возврат Значение; | Нет |
Оператор Возврат | С выражением | Без выражения: Возврат; — просто выход |
| Вызов в выражении | Х = Имя() — можно | Нельзя, ошибка компиляции модуля |
| Вызов отдельной строкой | Можно, результат отбросится | Обычный способ вызова |
Два момента, о которых часто спорят новички.
Можно ли вызвать функцию как процедуру? Да. Строка ЗаписатьВЖурнал(Текст); без присваивания допустима, даже если ЗаписатьВЖурнал — функция. Результат просто потеряется. Иногда так и задумано, но если функция возвращает признак успеха, игнорировать его — плохая идея.
Может ли процедура что-то «вернуть»? Формально нет. На практике результат из процедуры передают через параметры. По умолчанию параметры в 1С передаются по ссылке: если процедура присвоит параметру новое значение, оно окажется в переменной вызывающего кода. Так работают типовые обработчики событий:
Процедура ПередЗаписью(Отказ)
Если Не ЗначениеЗаполнено(Контрагент) Тогда
Отказ = Истина; // «ответ» платформе через параметр
КонецЕсли;
КонецПроцедуры
Чтобы параметр нельзя было изменить снаружи, его объявляют с Знач: Функция Посчитать(Знач Сумма). Внутри функции с ним можно работать как угодно, а переменная в вызывающем коде останется прежней.
Какого типа бывает результат функции
Вернуть можно значение любого типа, который существует в контексте выполнения. На практике чаще всего встречаются такие.
| Тип | Пример | Когда уместен |
|---|---|---|
| Число, Строка, Дата, Булево | Возврат Истина; | Проверки, расчёты, форматирование |
Неопределено | Возврат Неопределено; | «Ничего не нашли» |
| Ссылка на объект | Справочники.Номенклатура.НайтиПоКоду("00-001") | Поиск элемента, подбор по условию |
Структура | Новый Структура("Сумма, НДС", 100, 20) | Нужно вернуть несколько значений |
Массив | список ссылок, строк | Набор однотипных элементов |
ТаблицаЗначений | Запрос.Выполнить().Выгрузить() | Табличные данные на сервере |
Соответствие | ключ → значение | Быстрый поиск по ключу |
| Объект | Документ.ПолучитьОбъект() | Дальнейшее изменение и запись |
Как вернуть несколько значений сразу
Возврат принимает одно выражение. Если нужно отдать сумму, скидку и текст ошибки, их упаковывают в Структура:
Функция РассчитатьСкидку(Сумма, ПроцентСкидки)
Результат = Новый Структура("Скидка, КОплате", 0, Сумма);
Если ПроцентСкидки <= 0 Тогда
Возврат Результат;
КонецЕсли;
Результат.Скидка = Окр(Сумма * ПроцентСкидки / 100, 2);
Результат.КОплате = Сумма - Результат.Скидка;
Возврат Результат;
КонецФункции
Расчёт = РассчитатьСкидку(1000, 7);
// Расчёт.Скидка = 70, Расчёт.КОплате = 930
Структура удобнее массива: к полю обращаются по имени (Расчёт.Скидка), а не по номеру (Расчёт[0]), и через полгода код читается без комментариев.
Второй вариант — выходные параметры по ссылке, как в процедуре ПередЗаписью. Функция возвращает Булево (получилось или нет), а данные кладёт в параметр. Этот приём использует сама платформа, о нём ниже.
Как обрабатывать результат вызова без сюрпризов
Получили значение — проверьте его, прежде чем использовать. Особенно если функция ищет что-то в базе.
Проверка на заполненность
Универсальная проверка — ЗначениеЗаполнено(). Она возвращает Ложь для Неопределено, пустой ссылки, нуля, пустой строки и пустой даты.
Товар = Справочники.Номенклатура.НайтиПоКоду(Код);
Если Не ЗначениеЗаполнено(Товар) Тогда
Сообщить("Товар с кодом " + Код + " не найден");
Возврат;
КонецЕсли;
НайтиПоКоду и НайтиПоНаименованию при неудаче возвращают пустую ссылку, а не Неопределено. Сравнение Если Товар = Неопределено тут не сработает никогда — условие всегда будет ложным.
Проверка типа
Если функция в разных ветках отдаёт разные типы, сверяйте тип явно:
Значение = ПолучитьНастройку("ЛимитСкидки");
Если ТипЗнч(Значение) = Тип("Число") Тогда
Лимит = Значение;
Иначе
Лимит = 0;
КонецЕсли;
Защитные возвраты в начале функции
Хороший стиль — сначала отсечь некорректные входные данные, а потом писать основную логику без лишней вложенности:
Функция ЦенаСНДС(Цена, СтавкаНДС)
Если Цена <= 0 Тогда
Возврат 0;
КонецЕсли;
Возврат Окр(Цена * (100 + СтавкаНДС) / 100, 2);
КонецФункции
Для цены 500 и ставки 20 получится 600 — проверьте: 500 × 120 / 100 = 600.
Типичные ошибки с результатом функций
Эти ошибки не дают сообщений при компиляции и всплывают уже у пользователя.
- ❌ Забыли
Возвратв одной из веток. Функция вернётНеопределено, а дальше код упадёт на арифметике или обращении к полю. Проверьте, что каждая веткаЕсли … ИначеЕсли … Иначезаканчивается возвратом либо что после условия есть общийВозврат. - ❌ Сравнение с
Неопределеновместо проверки пустой ссылки. Разобрано выше: используйтеЗначениеЗаполнено(). - ❌ Функция незаметно меняет входной параметр. Параметр передан по ссылке, внутри ему присвоили новое значение — и у вызывающего кода «испортилась» переменная. Защита —
Значв объявлении. - ❌ Изменение коллекции, полученной из кэша. Общий модуль с повторным использованием отдаёт одно и то же значение всем вызовам. Если один вызов добавит элемент в такой массив, остальные увидят изменённые данные. Из таких модулей возвращают
ФиксированныйМассивиФиксированнаяСтруктура. - ❌ Возврат на клиент того, что там не существует. Подробнее — в следующем разделе.
- ❌ Выход из функции до освобождения ресурсов.
ВозвратвнутриПопыткапрерывает выполнение сразу: если ниже стоит закрытие файла или фиксация транзакции, до них дело не дойдёт. Планируйте выход так, чтобы ресурсы освобождались в любой ветке.
Где платформа сама возвращает значения: стандартные механизмы
Многие встроенные методы — тоже функции. Знать, что именно они отдают при неудаче, полезнее, чем помнить синтаксис.
| Метод | Что возвращает | Если не нашёл |
|---|---|---|
Справочники.Х.НайтиПоКоду() | Ссылку | Пустую ссылку |
Массив.Найти(Значение) | Индекс элемента | Неопределено |
ТаблицаЗначений.Найти() | Строку таблицы | Неопределено |
Структура.Свойство(Ключ, Значение) | Булево | Ложь, значение не меняется |
СтрНайти(Строка, Подстрока) | Позицию символа | 0 |
Запрос.Выполнить() | РезультатЗапроса | Пустой результат, проверяется Пустой() |
Структура.Свойство — возврат через параметр
Этот метод показывает оба способа сразу: результатом функции служит признак Истина/Ложь, а само значение уходит во второй параметр.
Контрагент = Неопределено;
Если Параметры.Свойство("Контрагент", Контрагент) И ЗначениеЗаполнено(Контрагент) Тогда
Объект.Контрагент = Контрагент;
КонецЕсли;
Результат диалога и закрытия формы
В управляемом интерфейсе модальные окна по умолчанию отключены, поэтому ответ пользователя приходит не как результат функции, а в процедуру-обработчик через ОписаниеОповещения:
&НаКлиенте
Процедура Удалить(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеОтвета", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Удалить строку?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеОтвета(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
// удаляем
КонецЕсли;
КонецПроцедуры
С платформы 8.3.18 есть асинхронный вариант: процедура помечается Асинх, а ответ ждут оператором Ждать — Ответ = Ждать ВопросАсинх("Удалить строку?", РежимДиалогаВопрос.ДаНет);. Код снова читается сверху вниз, как обычный вызов функции.
Своя форма возвращает результат так же: при закрытии вызывают Закрыть(Значение), и это значение получит обработчик оповещения о закрытии в открывающей форме.
Повторное использование возвращаемых значений
У общего модуля есть свойство «Повторное использование возвращаемых значений» с вариантами «На время вызова» и «На время сеанса». Платформа запоминает результат функции для одинаковых параметров и при повторном вызове отдаёт сохранённое значение, не выполняя код снова.
Так кэшируют редко меняющиеся вещи: настройки учёта, константы, права пользователя. Кэш хранится ограниченное время, а сбросить его досрочно можно методом ОбновитьПовторноИспользуемыеЗначения().
Практические примеры: клиент, сервер, запросы
Функция на сервере, вызов с клиента
Значение, которое серверная функция возвращает на клиент, передаётся по сети. Поэтому тип должен существовать на клиенте: примитивы, ссылки, Структура, Массив, Соответствие подходят. ТаблицаЗначений, РезультатЗапроса, объекты документов на тонкий клиент не передаются.
&НаКлиенте
Процедура ПроверитьОстаток(Команда)
Остаток = ОстатокНаСервере(Объект.Номенклатура);
Сообщить("Остаток: " + Формат(Остаток, "ЧН=0; ЧГ=0"));
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОстатокНаСервере(Номенклатура)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Остатки.КоличествоОстаток), 0) КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК Остатки";
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Количество;
КонецЕсли;
Возврат 0;
КонецФункции
Имя регистра здесь условное — в вашей конфигурации оно может быть другим. Важен принцип: на клиент уходит одно число, а не таблица остатков.
Если таблицу всё же нужно передать, её превращают в массив структур или кладут во временное хранилище: Адрес = ПоместитьВоВременноеХранилище(Таблица, УникальныйИдентификатор); — и возвращают строку Адрес.
Функция-проверка для условия
Функция ДокументМожноИзменять(ДатаДокумента, ДатаЗапрета)
Возврат Не ЗначениеЗаполнено(ДатаЗапрета) Или ДатаДокумента > ДатаЗапрета;
КонецФункции
Функция возвращает Булево, и её вызов читается как обычная фраза: Если ДокументМожноИзменять(Дата, ДатаЗапрета) Тогда. Логика в одном месте, а не размазана по пяти модулям.
Тернарный оператор как короткий возврат
Когда результат зависит от одного условия, ?() экономит строки:
Функция ТекстСтатуса(Проведен)
Возврат ?(Проведен, "Проведён", "Черновик");
КонецФункции
Как сделать код быстрее и чище с помощью возвращаемых значений
Правильно спроектированный результат функции влияет и на скорость, и на поддержку кода.
- ✅ Один серверный вызов вместо нескольких. Каждый вызов с клиента на сервер — сетевой запрос. Если нужны остаток, цена и единица измерения, верните
Структураза один вызов, а не дёргайте три функции подряд. Вызов серверной функции в цикле на клиенте — частая причина «тормозов» формы. - ✅ Сохраняйте результат в переменную.
Если ПолучитьЦену(Товар) > 0 Тогда Сумма = ПолучитьЦену(Товар) * Количество— два обращения к базе. Присвойте цену переменной один раз. - ✅ Возвращайте ссылку, а не объект.
ПолучитьОбъект()читает из базы весь объект с табличными частями. Если нужен только реквизит, получите его запросом или через ссылку. - ✅ Кэшируйте стабильные данные. Настройки, которые читаются в каждом документе, вынесите в общий модуль с повторным использованием.
- ✅ Одна функция — одна задача и один тип результата. Функция
НайтиКонтрагентавозвращает ссылку или пустую ссылку. Не надо смешивать в ней ссылку, строку с текстом ошибки иЛожь. - ✅ Защитные возвраты вместо вложенных
Если. Три проверки в начале сВозвратчитаются легче, чем три уровня вложенности.
Найти места, где функция вызывается в цикле с клиента, помогает замер производительности в режиме отладки: он показывает, какие строки выполнялись дольше всего и сколько раз.
Частые вопросы
Можно ли вернуть значение из обработчика события формы?
Отказ = Истина или СтандартнаяОбработка = Ложь.Чем `Возврат;` в процедуре отличается от `Возврат` в функции?
Возврат указывают выражение, которое станет результатом вызова.Можно ли вернуть из функции другую функцию?
ОписаниеОповещения или имя метода строкой, но второй вариант требует Выполнить() и небезопасен.Почему серверная функция вернула на клиент пустое значение вместо таблицы?
ТаблицаЗначений недоступен на тонком клиенте. Верните массив структур или адрес во временном хранилище.