Почему возникает ошибка «Значение не является значением объектного типа» при выполнении запроса?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Рассмотрим весьма распространенную и коварную ситуацию при разработке в 1С. Часто бывает так: мы пишем запрос, проверяем его в консоли или во внешней обработке — все работает идеально. Но как только переносим этот же код в модуль формы документа (или справочника), программа внезапно выдает ошибку: Значение не является значением объектного типа (Выбрать). Проанализируем эту проблему подробно, разберем код по шагам и выясним истинные причины такого поведения платформы.

Посмотрим на пример проблемного кода

Разберем исходный код, который вызвал затруднения. Разработчик пытается получить данные из среза последних регистра сведений и обработать результат:


Запрос = Новый Запрос; 
Запрос.Текст = "ВЫБРАТЬ 
| ДислокацииВагоновСрезПоследних.ДатаСледующегоРемонта 
|ИЗ 
| РегистрСведений.ДислокацииВагонов.СрезПоследних(, Вагон = &Вагон) КАК ДислокацииВагоновСрезПоследних"; 

Запрос.УстановитьПараметр("Вагон", Вагон.НомерВагона); 
РезультатЗапроса = Запрос.Выполнить; 
Выборка = РезультатЗапроса.Выбрать; 

Если РезультатЗапроса.Пустой Тогда 
    Возврат Истина; 
КонецЕсли; 

Платформа останавливает выполнение на строке Выборка = РезультатЗапроса.Выбрать; и сообщает, что значение не является объектом. Выясним причину этой ошибки. На самом деле здесь кроется сразу несколько грубых недочетов, каждый из которых мы рассмотрим подробнее.

Причина 1: Конфликт имен (Shadowing) переменной и реквизита формы

Проанализируем ситуацию, с которой столкнулся автор. При пошаговой отладке выяснилось, что РезультатЗапроса в данном контексте — это не переменная, в которую помещается объект результата запроса, а реквизит формы с типом Строка.

В программировании на платформе 1С существует понятие перекрытия областей видимости (shadowing). Когда мы пишем код в модуле формы, платформа позволяет обращаться к реквизитам этой формы напрямую, без указания контекста ЭтотОбъект. Таким образом, когда разработчик написал РезультатЗапроса = Запрос.Выполнить;, он не создал новую локальную переменную, а попытался перезаписать существующий строковой реквизит формы.

Чтобы избежать подобных ловушек, рассмотрим стандарты разработки 1С. Они строго предписывают:

  1. Не называть реквизиты форм базовыми техническими терминами (такими как Запрос, РезультатЗапроса, Выборка, Таблица). Реквизиты должны отражать бизнес-логику (например, ТекстРезультатаОбработки).
  2. Для локальных переменных использовать стандартные и предсказуемые имена. Для запросов это обычно РезультатЗапроса, а для выборки — ВыборкаДетальныеЗаписи.

Причина 2: Жесткая типизация реквизитов формы

Посмотрим на механику присвоения значений в 1С. Вы можете задаться вопросом: почему при выполнении оператора присваивания строковой реквизит просто не изменил свой тип на объект результата запроса? Ответ кроется в жесткой типизации реквизитов формы.

Если реквизит формы РезультатЗапроса был создан с конкретным примитивным типом Строка, присвоить ему сложный объект (результат выполнения запроса) нельзя. Платформа в такой ситуации пытается либо привести значение к строковому представлению, либо оставляет значение пустым. В итоге, в переменной РезультатЗапроса остается строка. Когда код переходит к следующей строке и пытается через оператор «точка» вызвать метод Выбрать, платформа выдает ошибку, так как у примитивного типа данных нет такого метода.

Причина 3: Грубая синтаксическая ошибка (отсутствие скобок)

Разберем еще один критический момент в исходном коде, на который стоит обратить особое внимание. Обратите внимание на следующие строки:


РезультатЗапроса = Запрос.Выполнить; 
Выборка = РезультатЗапроса.Выбрать; 
Если РезультатЗапроса.Пустой Тогда

В языке 1С Выполнить(), Выбрать() и Пустой() — это методы (функции) объектов, а не их свойства. Их вызов обязательно должен сопровождаться круглыми скобками. Попытка обратиться к ним без скобок воспринимается платформой как попытка прочитать свойство с таким именем.

Даже если бы мы исправили проблему с именем переменной, этот код все равно завершился бы с ошибкой. Правильный синтаксис всегда требует наличия скобок для вызова функций:


РезультатЗапроса = Запрос.Выполнить(); 
Выборка = РезультатЗапроса.Выбрать(); 

Если РезультатЗапроса.Пустой() Тогда 
    Возврат Истина; 
КонецЕсли; 

Причина 4: Контекст исполнения (Клиент / Сервер)

Рассмотрим еще один нюанс, который часто приводит к ошибке Значение не является значением объектного типа. В обсуждаемом примере использовались «Обычные формы». Однако, если вы попытаетесь выполнить подобный код в современной конфигурации на «Управляемых формах» на стороне клиента, вы получите ошибку.

Проанализируем почему это происходит. В архитектуре управляемого приложения классы Запрос, РезультатЗапроса и ВыборкаИзРезультатаЗапроса физически не существуют и недоступны на стороне тонкого клиента. Вся работа с базой данных и запросами должна выполняться исключительно на сервере.

Если вы хотите выполнить запрос в управляемой форме, вам необходимо вынести этот код в отдельную серверную процедуру или функцию, используя директивы компиляции:


&НаКлиенте
Процедура ПроверитьВагон()
    Результат = ПроверитьВагонНаСервере();
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПроверитьВагонНаСервере()
    Запрос = Новый Запрос;
    Запрос.Текст = "...";
    // выполнение запроса
    Возврат Истина;
КонецФункции

Подведем итоги: как избежать ошибки

Чтобы больше никогда не сталкиваться с подобными ситуациями, сформулируем основные правила при написании кода:

  1. Проверяйте пересечения имен: Убедитесь, что имена ваших локальных переменных в модуле не совпадают с именами реквизитов формы, табличных частей или реквизитов самого объекта (документа/справочника).
  2. Используйте скобки для методов: Всегда пишите Выполнить() и Выбрать() со скобками.
  3. Соблюдайте клиент-серверную архитектуру: Помните, что объекты базы данных и запросы недоступны в контексте тонкого клиента.
  4. Используйте отладчик: Если вы получаете сообщение об ошибке при обращении через точку, остановите код в отладчике на строку выше и проверьте тип значения переменной. Если там Строка, Неопределено или Null, значит, ошибка найдена — вы пытаетесь применить объектный метод к примитивному или пустому значению.
← На главную