Каждый разработчик на платформе «1С:Предприятие 8» рано или поздно сталкивается с системным сообщением: «Значение не является значением объектного типа». Эта ошибка является классическим исключением времени выполнения (runtime error). Она сигнализирует о том, что программа пытается обратиться к свойству или методу переменной, которая не содержит в себе объект. В подавляющем большинстве случаев это означает, что в переменной находится значение Неопределено (Undefined) или Null.
В данной статье мы подробно разберем, почему возникает эта ошибка при попытке получить полное имя объекта метаданных (например, справочника, на который ссылается реквизит документа), проанализируем уязвимости стандартных подходов и напишем надежный, быстрый и архитектурно правильный код.
Рассмотрим исходный фрагмент кода, который вызвал затруднения у автора темы на форуме:
Для Каждого Реквизит Из ВыбранныйДокумент.Метаданные.Реквизиты Цикл
Тип = ТипЗнч(ВыбранныйДокумент[Реквизит.Имя]);
МТ = Метаданные.НайтиПоТипу(Тип).ПолноеИмя;
Сообщить(МТ);
КонецЦикла;
На первый взгляд, логика кажется вполне понятной: мы перебираем реквизиты документа, получаем тип значения конкретного реквизита, находим объект метаданных по этому типу и пытаемся вывести его полное имя (например, Справочник.Валюты).
Однако при запуске этого кода платформа аварийно завершает работу с ошибкой: «Значение не является значением объектного типа (ПолноеИмя)».
Проанализируем ситуацию по шагам. Ошибка возникает из-за того, что метод Метаданные.НайтиПоТипу() не всегда возвращает объект метаданных. Если переданный тип относится к простым типам данных (таким как Число, Строка, Дата или Булево), платформа не может сопоставить его ни с одним прикладным объектом конфигурации. В результате метод возвращает пустое значение — Неопределено.
Когда код доходит до конструкции:
МТ = Метаданные.НайтиПоТипу(Тип).ПолноеИмя;
он фактически пытается выполнить следующую операцию для простых типов:
МТ = Неопределено.ПолноеИмя;
Поскольку у системного значения Неопределено нет свойства (или метода) ПолноеИмя, платформа немедленно прекращает выполнение операции и выбрасывает исключение.
Хотя простое добавление проверки на Неопределено решает проблему падения программы, исходный код автора содержит несколько серьезных методологических ошибок, которые могут привести к багам в реальных бизнес-приложениях. Разберем их подробнее.
1. Избыточная нагрузка на базу данных
Выражение ВыбранныйДокумент[Реквизит.Имя] обращается непосредственно к данным конкретной записи в СУБД. Если этот код выполняется в цикле по множеству документов или если объект документа не был предварительно кэширован в оперативной памяти, платформа будет совершать неявные запросы к базе данных для извлечения значений полей. Для решения чисто конфигурационной задачи (определение структуры метаданных) обращаться к фактическим транзакционным данным — грубая ошибка производительности.
2. Ловушка незаполненных реквизитов составного типа
Представим, что реквизит документа имеет составной тип данных, например, он может содержать ссылку на СправочникСсылка.Контрагенты или на СправочникСсылка.ФизическиеЛица. Если в конкретном проверяемом документе это поле не заполнено, функция ТипЗнч() вернет либо тип Неопределено, либо пустую ссылку одного из типов. Таким образом, вы никогда не узнаете весь список типов, которые потенциально могут быть записаны в этот реквизит согласно структуре конфигурации.
3. Зависимость от конкретного экземпляра объекта
Код автора жестко привязан к конкретному документу (ВыбранныйДокумент). Если нам потребуется узнать типы реквизитов документа без создания или чтения самого документа (например, на этапе динамического анализа структуры метаданных конфигурации), этот код окажется абсолютно бесполезным.
Давайте составим четкий список типов данных, для которых метод Метаданные.НайтиПоТипу() гарантированно вернет значение Неопределено. Это поможет нам понять границы применимости данного метода:
Строка, Число, Дата, Булево. Эти типы являются базовыми для платформы и не описываются как отдельные прикладные объекты в дереве метаданных.УникальныйИдентификатор, ХранилищеЗначения, ДвоичныеДанные, ОписаниеТипов. Они служат для служебных целей платформы.СтрокаТабличнойЧасти.Товары. Они не имеют прямого сопоставления с объектами метаданных верхнего уровня (такими как справочники или документы) через данный метод.Еще одна важная деталь, на которую следует обратить внимание при разработке: в платформе «1С:Предприятие 8» у объектов метаданных ПолноеИмя — это именно метод (функция), а не простое свойство.
Правильный синтаксис вызова требует использования круглых скобок:
ИмяОбъекта = ОбъектМетаданных.ПолноеИмя();
Хотя в режиме обратной совместимости или в старых версиях платформы обращение без скобок могло работать (платформа неявно преобразовывала обращение к свойству в вызов метода), современные стандарты разработки фирмы «1С» строго предписывают использовать круглые скобки. Игнорирование этого правила может привести к неожиданному поведению при переходе на новые версии платформы или при работе в жестких режимах совместимости.
Чтобы написать по-настоящему надежный, быстрый и независимый от конкретных данных код, мы должны анализировать описание типов самого реквизита, а не значение в документе.
У любого объекта метаданных (будь то реквизит справочника, документа или бизнес-процесса) есть свойство Тип, имеющее системный тип ОписаниеТипов. С помощью метода ОписаниеТипов.Типы() мы можем получить массив всех возможных типов, назначенных этому реквизиту в конфигураторе.
Рассмотрим оптимальный и безопасный вариант кода на чистой платформе 1С:
// Получаем метаданные реквизитов документа напрямую из конфигурации
РеквизитыМД = Метаданные.Документы.ЗаказПокупателя.Реквизиты;
Для Каждого Реквизит Из РеквизитыМД Цикл
// Получаем массив всех допустимых типов для текущего реквизита
МассивТипов = Реквизит.Тип.Типы();
Для Каждого ТекТип Из МассивТипов Цикл
// Пытаемся найти объект метаданных по типу
ОбъектМД = Метаданные.НайтиПоТипу(ТекТип);
// Обязательно проверяем на Неопределено (отсекаем простые типы)
Если ОбъектМД <> Неопределено Тогда
// Выводим полное имя объекта конфигурации
Сообщить("Реквизит: " + Реквизит.Имя + " -> " + ОбъектМД.ПолноеИмя());
Иначе
// Обрабатываем простой или системный тип данных
Сообщить("Реквизит: " + Реквизит.Имя + " -> Простой тип: " + Строка(ТекТип));
КонецЕсли;
КонецЦикла;
КонецЦикла;
Преимущества данного алгоритма:
ОбъектМД <> Неопределено полностью гарантирует защиту от возникновения исключения «Значение не является значением объектного типа».Если вы ведете разработку в типовой конфигурации фирмы «1С» (например, «1С:ERP Управление предприятием», «1С:Комплексная автоматизация», «1С:Управление торговлей», «1С:Бухгалтерия» и др.), вам не нужно писать велосипеды и вручную обрабатывать проверки на Неопределено. В составе Библиотеки Стандартных Подсистем (БСП) уже есть готовые, оптимизированные и протестированные функции общего назначения.
Рекомендуется использовать методы общего модуля ОбщегоНазначения:
ОбщегоНазначения.ПолноеИмяОбъектаМетаданных(Тип) — принимает тип данных или ссылку и безопасно возвращает полное имя объекта метаданных (например, "Справочник.Валюты"). Если тип простой или системный, метод вернет пустую строку или Неопределено, не вызывая критической ошибки.ОбщегоНазначения.ИмяОбъектаМетаданных(Тип) — возвращает только краткое имя объекта метаданных (например, "Валюты").Посмотрим на пример использования функций БСП для решения нашей задачи:
Для Каждого Реквизит Из ВыбранныйДокумент.Метаданные.Реквизиты Цикл
Для Каждого ТекТип Из Реквизит.Тип.Типы() Цикл
// Безопасное получение полного имени через БСП
ПолноеИмя = ОбщегоНазначения.ПолноеИмяОбъектаМетаданных(ТекТип);
Если ЗначениеЗаполнено(ПолноеИмя) Тогда
Сообщить("Связанный прикладной объект: " + ПолноеИмя);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Использование БСП делает ваш код чище, лаконичнее и существенно снижает вероятность совершения ошибок при ручной обработке исключительных ситуаций.
Мы детально разобрали природу ошибки Значение не является значением объектного типа (ПолноеИмя), научились обходить ограничения платформы при работе с простыми типами данных и рассмотрели архитектурно верные подходы к анализу метаданных в 1С:Предприятии 8. Применяйте эти знания на практике, чтобы ваш код всегда оставался быстрым, безопасным и масштабируемым!