В 1С:Предприятие процедуры и функции — основа логики любой конфигурации. Но если с функциями всё очевидно (они обязательно возвращают значение через оператор Возврат), то с процедурами часто возникает путаница. Разработчики спорят: может ли процедура вернуть результат? Как правильно обрабатывать её вывод? И почему иногда вместо ожидаемых данных приходит Неопределено?
На самом деле процедуры в 1С 8.3 (как и в более ранних версиях) не имеют явного механизма возврата значений — в отличие от функций. Однако есть обходные пути: передача результата через параметры-ссылки, использование глобальных переменных или возвращение объектов с методами. В этой статье мы разберём все возможные сценарии, включая малоизвестный трюк с динамическим созданием структур прямо в теле процедуры, который экономит до 30% кода в типовых задачах.
1. Процедура vs функция: ключевые различия
Прежде чем говорить о возвращаемых значениях, важно понять фундаментальную разницу между процедурой и функцией в 1С:
- 🔹 Функция — всегда возвращает результат через
Возврат [Значение]. Без этого оператора вернётсяНеопределено. - 🔹 Процедура — не имеет механизма возврата. Её задача — выполнить действия (изменить данные, записать документ, обновить регистр).
Но на практике границы стираются. Например, процедура может:
- 📝 Модифицировать переданные по ссылке параметры (через ключевое слово
Перем). - 🌍 Изменять глобальные переменные или реквизиты формы.
- 🔄 Возвращать объект, если он был создан внутри процедуры (например,
СтруктураилиМассив).
Пример классической функции и её аналога-процедуры:
// Функция (явный возврат)
Функция СуммаЧисел(Число1, Число2)
Возврат Число1 + Число2;
КонецФункции
// Процедура (возврат через параметр по ссылке)
Процедура СуммаЧиселПр(Число1, Число2, Перем Результат)
Результат = Число1 + Число2;
КонецПроцедуры
2. Способы "возврата" данных из процедуры
Хотя синтаксис 1С не предусматривает оператор Возврат для процедур, есть несколько легальных способов получить результат их работы. Рассмотрим каждый с примерами.
2.1. Параметры по ссылке (ключевое слово Перем)
Самый распространённый метод — передача переменной по ссылке. В этом случае процедура изменяет её значение напрямую.
Синтаксис:
Процедура ИмяПроцедуры(Параметр1, Перем Параметр2)
// Логика процедуры
Параметр2 = НовоеЗначение; // Изменяем переданную переменную
КонецПроцедуры
Пример с расчётом скидки:
Перем Скидка;
РассчитатьСкидку(СуммаЗаказа, Скидка);
// Теперь в переменной Скидка лежит результат
Процедура РассчитатьСкидку(Сумма, Перем ПроцентСкидки)
Если Сумма > 10000 Тогда
ПроцентСкидки = 10;
ИначеЕсли Сумма > 5000 Тогда
ПроцентСкидки = 5;
Иначе
ПроцентСкидки = 0;
КонецЕсли;
КонецПроцедуры
Используйте параметры по ссылке для возврата нескольких значений из одной процедуры. Например, можно вернуть и сумму, и процент скидки, и итоговую цену.
2.2. Возврат объектов (Структура, Массив, ТаблицаЗначений)
Процедура может создавать и возвращать объекты, если они формируются внутри её тела. Это работает потому, что объекты в 1С — ссылочный тип данных.
Пример с возвратом структуры:
Результат = ПолучитьДанныеОКлиенте(КодКлиента);
Процедура ПолучитьДанныеОКлиенте(Код)
Данные = Новый Структура;
Данные.Вставить("Имя", Справочники.Клиенты.НайтиПоКоду(Код).Наименование);
Данные.Вставить("Баланс", Справочники.Клиенты.НайтиПоКоду(Код).Баланс);
Данные.Вставить("ДатаРегистрации", Справочники.Клиенты.НайтиПоКоду(Код).ДатаРегистрации);
// Возвращаем объект (неявно)
КонецПроцедуры
Важно: в этом случае процедура не возвращает объект напрямую — она создаёт его в памяти, и вы получаете ссылку на него. Если не присвоить результат переменной (как в примере выше), объект будет утерян.
2.3. Глобальные переменные и реквизиты форм
Менее элегантный, но рабочий способ — использование глобальных переменных или реквизитов формы. Подходит для простых сценариев, но чреват ошибками при параллельной работе пользователей.
Пример с глобальной переменной:
Перем глРезультат;
ВыполнитьЗапрос();
Сообщить(глРезультат);
Процедура ВыполнитьЗапрос()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ СУММА(Сумма) КАК Итого ИЗ Документ.Реализация";
РезультатЗапроса = Запрос.Выполнить();
глРезультат = РезультатЗапроса.Выгрузить()[0].Итого;
КонецПроцедуры
Избегайте глобальных переменных в клиент-серверных решениях. Они могут привести к конфликтам данных при одновременной работе нескольких пользователей.
3. Что на самом деле возвращает процедура: таблица типов
Если попробовать присвоить результат выполнения процедуры переменной, 1С вернёт Неопределено. Но внутри процедуры могут формироваться данные, которые вы получите косвенно. В таблице ниже — все возможные сценарии:
| Способ "возврата" | Тип данных | Пример кода | Когда использовать |
|---|---|---|---|
| Параметр по ссылке | Любой (Число, Строка, Дата и т.д.) | Процедура Тест(Перем Рез) Рез = 10; |
Для возврата 1-2 значений |
| Создание объекта | Структура, Массив, ТаблицаЗначений |
Рез = Процедура(); // Внутри создаётся Структура |
Для возврата сложных данных |
| Глобальная переменная | Любой | Перем глРез; Процедура(); Сообщить(глРез); |
Для простых сценариев (не рекомендуется) |
| Изменение реквизита формы | Любой | Процедура ОбновитьИтог() Итог = 100; |
Для работы с формами |
| Неявный возврат | Неопределено |
Рез = Процедура(); // Рез = Неопределено |
Если не используются другие методы |
Обратите внимание: единственный способ вернуть из процедуры несколько разнотипных значений — использовать Структуру или Массив. Например, так можно вернуть и результат запроса, и статус его выполнения:
Данные = ПолучитьДанныеИСтатус();
Процедура ПолучитьДанныеИСтатус()
Результат = Новый Структура;
Попытка
Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 10 ИЗ Справочник.Номенклатура");
Результат.Вставить("Данные", Запрос.Выполнить().Выгрузить());
Результат.Вставить("Статус", Истина);
Исключение
Результат.Вставить("Статус", Ложь);
Результат.Вставить("Ошибка", ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
4. Типичные ошибки при работе с возвращаемыми значениями
Даже опытные разработчики иногда допускают ошибки при попытке получить данные из процедуры. Разберём самые распространённые случаи.
4.1. Попытка присвоить результат процедуры переменной
Классическая ошибка новичков:
Результат = МояПроцедура(); // Результат будет Неопределено!
Процедура не возвращает значение явно, поэтому такой код всегда вернёт Неопределено. Исправляйте через параметры по ссылке или объекты.
4.2. Забытый оператор Перем для параметра
Если не указать Перем при передаче параметра, процедура не сможет изменить его значение:
Процедура Тест(Значение) // Без "Перем"!
Значение = 10; // Изменение не отразится снаружи
КонецПроцедуры
4.3. Утечка памяти при возврате объектов
Если процедура создаёт объект (например, ТаблицаЗначений), но вы не присваиваете его переменной, объект останется в памяти как "мусор":
Процедура СоздатьТаблицу()
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Имя");
// Объект не возвращается и не используется!
КонецПроцедуры
Как проверить утечки памяти в 1С?
Используйте Тестирование и исправление (меню Отладка → Тестирование и исправление) с включённой опцией Поиск утечек памяти. Инструмент покажет все объекты, на которые нет ссылок, но которые не были удалены сборщиком мусора.
Убедитесь, что все параметры для возврата помечены как Перем|Проверьте, что объекты (Структура/Массив) присваиваются переменным|Используйте Попытка-Исключение для обработки ошибок|Избегайте глобальных переменных в серверных процедурах-->
5. Когда использовать процедуры вместо функций
Если процедуры так ограничены в возврате данных, почему бы всегда не использовать функции? Есть случаи, когда процедуры предпочтительнее:
- 🔧 Изменение состояния системы (запись документа, обновление регистра). Функция здесь неуместна, так как её задача — вернуть результат, а не менять данные.
- 📊 Выполнение побочных эффектов (отправка письма, печать чека, выгрузка файла).
- 🔄 Работа с формами. Процедуры-обработчики событий (
ПриОткрытии,ПриЗаписи) не могут быть функциями. - 🛠️ Модификация параметров. Если задача — изменить переданные данные (например, отфильтровать массив), процедура с
Перемудобнее.
Пример уместного использования процедуры:
Процедура ОбновитьЦеныНоменклатуры(Перем ТаблицаЦен)
Для Каждого Строка Из ТаблицаЦен Цикл
Если Строка.Номенклатура.ЭтоГруппа() Тогда
Продолжить;
КонецЕсли;
Строка.Цена = Строка.Цена * 1.1; // Увеличиваем цену на 10%
КонецЦикла;
КонецПроцедуры
Функции подходят для вычислений и получения данных, процедуры — для действий. Если ваш метод и делает что-то, и возвращает результат, разделите его на две части: процедуру для действий и функцию для возврата.
6. Продвинутые техники: динамический возврат данных
Для опытных разработчиков есть несколько нетривиальных способов возврата данных из процедур, которые выходят за рамки стандартных подходов.
6.1. Возврат через исключения
Можно "возвращать" данные, бросая исключения с нужной информацией. Этот метод используется редко, но полезен для обработки ошибок с дополнительными данными:
Попытка
ПроверитьДокумент(Док);
Исключение
Если ТипЗнч(ОписаниеОшибки().Причина) = Тип("Структура") Тогда
ДанныеОбОшибке = ОписаниеОшибки().Причина;
// Обрабатываем данные
КонецЕсли;
КонецПопытки;
Процедура ПроверитьДокумент(Док)
Если НЕ Док.Проведен Тогда
Ошибка = Новый Структура;
Ошибка.Вставить("Причина", "Документ не проведён");
Ошибка.Вставить("ДатаДокумента", Док.Дата);
ВызватьИсключение Ошибка;
КонецЕсли;
КонецПроцедуры
Используйте этот метод только для обработки ошибок. Злоупотребление исключениями для передачи данных усложняет отладку.
6.2. Возврат через контекст выполнения
В клиент-серверных решениях можно возвращать данные через контекст выполнения (например, реквизиты формы или менеджера значения). Это актуально для обработчиков событий:
Процедура ПриОткрытии()
Данные = ПолучитьДанныеДляФормы();
ЭтотОбъект.РеквизитФормы.Данные = Данные; // Возврат через реквизит
КонецПроцедуры
6.3. Использование функциональных опций (1С:Предприятие 8.3.20+)
В новых версиях платформы появились функциональные опции, которые позволяют гибко управлять логикой. Их можно использовать для условного возврата данных:
Процедура ПолучитьНастройкиПользователя(Перем Настройки)
Если ФункциональныеОпции.ПоказыватьРасширенныеНастройки.Включена() Тогда
Настройки = ПолучитьРасширенныеНастройки();
Иначе
Настройки = ПолучитьБазовыеНастройки();
КонецЕсли;
КонецПроцедуры
7. Оптимизация кода: когда процедуры эффективнее функций
В некоторых случаях процедуры дают выигрыш в производительности или читаемости кода по сравнению с функциями. Рассмотрим ключевые сценарии.
7.1. Работа с большими объёмами данных
Если метод модифицирует большой массив или таблицу значений, процедура с параметром Перем работает быстрее, чем функция, возвращающая новый объект:
// Медленно (создаётся копия массива)
Массив = ОбработатьМассивФункция(ИсходныйМассив);
// Быстро (изменяется исходный массив)
ОбработатьМассивПроцедура(ИсходныйМассив);
7.2. Цепочки вызовов
Процедуры удобнее для последовательных операций над одним объектом:
Документ = Документы.Реализация.СоздатьДокумент();
ЗаполнитьШапку(Документ);
ЗаполнитьТабличнуюЧасть(Документ);
РассчитатьСуммы(Документ);
ЗаписатьДокумент(Документ);
Здесь каждая процедура модифицирует один и тот же объект, что логичнее, чем передавать его через цепочку функций.
7.3. Асинхронные операции
В фоновых заданиях или при работе с Планировщик процедуры предпочтительнее, так как они не блокируют выполнение ожиданием возврата:
Планировщик.ВыполнитьПроцедуру("ОбновитьКурсыВалют", , Ложь); // Не блокирует основной поток
Для фоновых задач всегда используйте процедуры. Функции в асинхронном режиме могут привести к неожиданным ошибкам из-за попытки вернуть результат в несуществующий контекст.
FAQ: Частые вопросы о возвращаемых значениях в 1С
Может ли процедура вернуть несколько значений?
Да, для этого используйте:
- 📌 Несколько параметров по ссылке (каждый с ключевым словом
Перем). - 📌 Структуру или Массив, в которые упакуете все нужные данные.
Пример со Структурой:
Результат = ПолучитьДанные();
// Результат.Поле1, Результат.Поле2 и т.д.
Почему моя процедура возвращает Неопределено, даже если внутри создаётся Массив?
Вы забыли присвоить возвращаемый объект переменной. Процедура не возвращает значение явно — она только создаёт его в памяти. Пример ошибки:
Процедура Тест()
Массив = Новый Массив;
Массив.Добавить(1);
// Нет присвоения переменной снаружи!
КонецПроцедуры
Рез = Тест(); // Рез = Неопределено
Исправьте так:
Процедура Тест(Перем Рез)
Рез = Новый Массив;
Рез.Добавить(1);
КонецПроцедуры
Как вернуть таблицу значений из процедуры без параметров по ссылке?
Создайте таблицу внутри процедуры и верните её через присваивание объекта переменной:
ТЗ = ПолучитьТаблицуДанных();
Процедура ПолучитьТаблицуДанных()
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Наименование");
// Заполняем ТЗ...
// Возвращаем неявно (ТЗ останется в памяти)
КонецПроцедуры
Важно: не забывайте присваивать результат переменной (ТЗ = ...), иначе таблица будет утеряна.
Можно ли из процедуры вернуть объект, созданный в другой процедуре?
Да, если объект является ссылочным типом (Структура, Массив, ТаблицаЗначений и т.д.). Пример:
Объект = СоздатьОбъект();
Процедура СоздатьОбъект()
Объект = Новый Структура;
Объект.Вставить("Ключ", "Значение");
// Объект будет доступен снаружи
КонецПроцедуры
Для примитивных типов (Число, Строка) это не сработает — они передаются по значению.
Как обработать ошибку, если процедура не возвращает статус выполнения?
Используйте блок Попытка-Исключение:
Попытка
ВыполнитьПроцедуру();
Исключение
ЗаписатьЖурналРегистрации("Ошибка", УровеньЖурнала.Ошибка,, ОписаниеОшибки());
КонецПопытки;
Или модифицируйте процедуру, чтобы она возвращала статус через параметр:
Процедура ВыполнитьПроцедуру(Перем Успех)
Попытка
// Логика процедуры
Успех = Истина;
Исключение
Успех = Ложь;
КонецПопытки;
КонецПроцедуры