Разработка и Конфигурация · 13 мин
Обращение к процедуре объекта как к функции в 1С: причины ошибки и способы исправления
Разбираем ошибку «Обращение к процедуре объекта как к функции» в 1С: причины, типичные случаи с методом Удалить, пошаговое исправление кода и проверка результата.
Ошибка «Обращение к процедуре объекта как к функции» — одно из самых частых сообщений компилятора встроенного языка 1С. Она возникает, когда разработчик пытается использовать результат вызова процедуры там, где ожидается значение: в правой части присваивания, в условии, в составе выражения или в качестве параметра другого метода. Процедура в 1С не возвращает значения, поэтому платформа останавливает выполнение и выводит соответствующее сообщение.
Частный и очень распространённый случай — метод Удалить(). Многие объекты 1С (элементы справочников, документы, записи регистров, элементы коллекций) имеют метод удаления, который является процедурой. Попытка написать что-то вроде Результат = СправочникОбъект.Удалить(); или Если Элементы.Удалить(Элемент) Тогда приводит именно к этой ошибке.
Разобраться в проблеме просто, если понимать разницу между процедурой и функцией во встроенном языке, знать, какие методы объектов возвращают значения, а какие — нет, и уметь читать текст сообщения об ошибке. Ниже приведены типичные сценарии, способы исправления и методы профилактики.
Чем процедура отличается от функции во встроенном языке 1С
Во встроенном языке 1С существуют два вида методов: процедуры и функции. Процедура выполняет действие и ничего не возвращает. Функция выполняет действие и обязательно возвращает значение через оператор Возврат. Это различие жёстко контролируется компилятором: синтаксис вызова одинаковый, но семантика разная.
Когда вы пишете А = МояФункция();, компилятор проверяет: является ли МояФункция функцией. Если это процедура — возникает ошибка «Обращение к процедуре как к функции». Аналогично работает проверка для методов объектов: платформа знает сигнатуру каждого встроенного метода и не позволяет использовать процедурный метод в позиции значения.
Обратная ситуация тоже возможна, но она не вызывает ошибки: функцию можно вызвать как процедуру, просто проигнорировав возвращаемое значение. Например, НайтиПоКоду("000000001"); без присваивания результата — допустимый код, хотя обычно это признак логической ошибки.
Типичные сценарии возникновения ошибки с методом Удалить
Присваивание результата удаления переменной
Самый частый случай — попытка проверить, успешно ли прошло удаление, через возвращаемое значение:
Результат = СправочникОбъект.Удалить(); // Ошибка!
Если Результат Тогда
Сообщить("Удалено");
КонецЕсли;
Метод Удалить() объекта справочника или документа — процедура. Он либо выполняется, либо вызывает исключение при ошибке (например, при нарушении ссылочной целостности). Правильный подход — вызвать метод отдельным оператором и обернуть его в попытку:
Попытка
СправочникОбъект.Удалить();
Сообщить("Объект удалён");
Исключение
Сообщить("Ошибка удаления: " + ОписаниеОшибки());
КонецПопытки;
Удаление элемента коллекции в условии
Вторая типичная ситуация — удаление элемента таблицы значений, массива или табличной части внутри условия или цикла с проверкой:
Если ТаблицаЗначений.Удалить(СтрокаТаблицы) Тогда // Ошибка!
...
КонецЕсли;
Метод Удалить() коллекций также является процедурой. Удаление либо происходит, либо вызывает исключение (например, если элемент не принадлежит коллекции). Проверять успешность через возвращаемое значение не нужно.
// Неправильно:
// Если Массив.Удалить(Индекс) Тогда ...
// Правильно:
Если Индекс >= 0 И Индекс < Массив.Количество() Тогда
Массив.Удалить(Индекс);
КонецЕсли;Как правильно удалять объекты и элементы коллекций
Для объектов базы данных (справочники, документы, планы счетов и т.д.) удаление выполняется методом Удалить() объекта, полученного через ПолучитьОбъект(). Метод не возвращает значения, а об успешности операции свидетельствует отсутствие исключения. Альтернатива — установка пометки удаления через УстановитьПометкуУдаления(Истина), что является более безопасной практикой: помеченные объекты удаляются штатной обработкой с контролем ссылочной целостности.
Для коллекций в памяти (массивы, таблицы значений, табличные части) метод Удалить() принимает либо индекс, либо сам элемент. Перед удалением по индексу проверяйте границы, при удалении в цикле — итерируйте коллекцию в обратном порядке, чтобы индексы не «съезжали»:
Для Инд = 1 - ТабличнаяЧасть.Количество() По 0 Цикл
СтрокаТЧ = ТабличнаяЧасть[-Инд];
Если СтрокаТЧ.Количество = 0 Тогда
ТабличнаяЧасть.Удалить(СтрокаТЧ);
КонецЕсли;
КонецЦикла;
- 1Найдите строку из сообщения об ошибке — платформа указывает модуль и номер строки
- 2Определите, какой метод вызывается: посмотрите его описание в синтакс-помощнике (процедура или функция)
- 3Если метод — процедура, уберите присваивание и вызовите его отдельным оператором
- 4Если результат нужен, найдите альтернативный метод-функцию (например, НайтиПоНаименованию вместо ручного обхода)
- 5Проверьте успешность операции через Попытка/Исключение или последующую проверку состояния
- 6Запустите конфигурацию и убедитесь, что ошибка исчезла, а логика работает как задумано
Другие методы-процедуры, которые часто вызывают как функции
Метод Удалить() — не единственный источник проблемы. Разработчики регулярно ошибаются и с другими методами-процедурами, ожидая от них возвращаемого значения. Ниже перечислены наиболее распространённые.
| Метод (процедура) | Типичная ошибка | Правильная альтернатива |
|---|---|---|
| Объект.Записать() | Если Объект.Записать() Тогда | Записать() + Попытка/Исключение |
| Объект.Удалить() | Результат = Объект.Удалить() | Удалить() + проверка через исключение |
| Объект.УстановитьПометкуУдаления() | Использование в условии | Вызов отдельным оператором |
| Коллекция.Очистить() | Присваивание результата | Вызов, затем проверка Количество() = 0 |
| Сообщить() | Возврат Сообщить(Текст) | Сообщить отдельно, Возврат отдельно |
Общий признак всех этих случаев: метод по смыслу выполняет действие, а не вычисляет значение. Если в синтакс-помощнике в описании метода нет раздела «Возвращаемое значение» — это процедура, и использовать её в выражениях нельзя.
Как проверить успешность удаления без возвращаемого значения
Раз метод Удалить() не возвращает признак успеха, возникает резонный вопрос: как узнать, что удаление действительно произошло? Есть три рабочих способа в зависимости от ситуации.
Первый — конструкция Попытка/Исключение. Если метод завершился без исключения, операция выполнена. Исключение при удалении объекта базы данных обычно означает нарушение ссылочной целостности или отсутствие прав. Текст ошибки доступен через функцию ОписаниеОшибки() внутри блока исключения.
Второй способ — последующая проверка состояния. Для коллекций: сравнить Количество() до и после удаления или проверить, что Найти() больше не находит элемент. Для объектов базы данных: попытаться получить объект по ссылке повторно и проверить результат.
Третий способ — для массовых удалений использовать пометку удаления вместо непосредственного удаления. Тогда контроль ссылочной целостности выполнит штатная обработка «Удаление помеченных объектов», а вы получите список объектов, которые удалить не удалось, в понятном виде.
Как проверить результат
Модуль сохраняется и проверяется без ошибок
Сообщение «Обращение к процедуре как к функции» с указанием строки
Метод Удалить() выполняется без исключения
Срабатывает блок Исключение с текстом ошибки
Количество() уменьшилось, элемент не находится
Элемент по-прежнему присутствует в коллекции
Повторное получение объекта по ссылке не даёт данных
Объект доступен, удаление не выполнено
Когда результат действительно нужен: переделка процедуры в функцию
Иногда ошибка возникает не из-за незнания встроенных методов, а из-за собственного кода: вы вызываете свою процедуру общего модуля как функцию. Здесь два пути. Если возвращаемое значение не нужно — уберите присваивание. Если нужно — измените объявление метода.
Переделка процедуры в функцию требует трёх действий: заменить ключевое слово Процедура на Функция, добавить оператор Возврат Значение; во всех ветках логики и убедиться, что существующие вызовы процедуры не сломаются. Вызов функции как процедуры допустим, поэтому старый код продолжит работать — обратная совместимость сохраняется.
// Было:
Процедура УдалитьУстаревшиеЗаписи(Период) Экспорт
...
КонецПроцедуры
// Стало:
Функция УдалитьУстаревшиеЗаписи(Период) Экспорт
КоличествоУдалённых = 0;
...
Возврат КоличествоУдалённых;
КонецФункции
Учитывайте ограничение: в клиент-серверной архитектуре функция, вызываемая с клиента на сервере, сериализует возвращаемое значение. Если результат не нужен вызывающей стороне, процедура предпочтительнее — меньше накладные расходы и яснее намерение кода.
Как выбрать действие
Профилактика: как не допускать эту ошибку
Основной инструмент профилактики — синтакс-помощник конфигуратора. Перед использованием незнакомого метода откройте его описание (Ctrl+F1 на имени метода) и посмотрите раздел «Возвращаемое значение». Если его нет — метод является процедурой.
Второй инструмент — контекстная подсказка редактора кода. При наборе имени метода после точки конфигуратор показывает список методов объекта с указанием типа. Функции и процедуры в подсказке различаются, что позволяет заметить несоответствие ещё до компиляции.
Третья мера — регулярная проверка модулей через «Конфигурация → Проверка конфигурации» с включённой проверкой логической целостности модулей. Ошибки обращения к процедурам как к функциям выявляются на этом этапе, ещё до запуска, что особенно важно для редко выполняемых веток кода, которые не попадают в ручное тестирование.
Частые вопросы
Почему метод Удалить() не возвращает признак успеха, как в других языках?
Во встроенном языке 1С принята модель исключений: если операция не удалась, платформа генерирует исключение с подробным описанием причины. Возвращаемый признак успеха дублировал бы этот механизм. Для обработки ошибок используйте Попытка/Исключение.
Можно ли вызвать функцию как процедуру, проигнорировав результат?
Да, это допустимо и не вызывает ошибки. Например, НайтиПоКоду() можно вызвать без присваивания результата. Однако если результат функции всегда игнорируется, это повод пересмотреть код — возможно, метод должен быть процедурой.
Как удалить элемент массива в цикле, чтобы не пропустить элементы?
Итерируйте массив в обратном порядке — от последнего индекса к первому. Тогда удаление текущего элемента не влияет на индексы ещё не обработанных. Альтернатива — собирать подлежащие удалению элементы в отдельный список и удалять их после основного цикла.
Чем непосредственное удаление объекта отличается от пометки удаления?
Метод Удалить() удаляет объект сразу и вызывает исключение при нарушении ссылочной целостности. УстановитьПометкуУдаления(Истина) только помечает объект; физическое удаление выполняет обработка «Удаление помеченных объектов» с контролем ссылок и возможностью отмены. Для прикладных решений пометка удаления — более безопасный путь.
Ошибка появляется только у части пользователей. Почему?
Код с ошибкой компилируется при первом обращении к нему. Если проблемная ветка выполняется только при определённых условиях или правах, у остальных пользователей ошибка не проявится. Исправьте код и выполните полную проверку конфигурации, чтобы найти все подобные места.
Как быстро найти все места, где процедура вызывается как функция?
Используйте «Конфигурация → Проверка конфигурации» с отмеченной проверкой модулей — компилятор перечислит все ошибки с указанием модуля и строки. Дополнительно можно выполнить глобальный поиск имени процедуры и просмотреть контекст каждого вызова.
Что важно запомнить
Процедуры в 1С не возвращают значений, поэтому их нельзя использовать в выражениях. Метод Удалить() — процедура: вызывайте его отдельным оператором, а успешность проверяйте через Попытка/Исключение или последующую проверку состояния. Если результат нужен из собственного кода — переделайте процедуру в функцию. Регулярная проверка конфигурации выявляет такие ошибки до их проявления у пользователей.