Как исправить ошибку "Значение не является значением объектного типа (ИсточникИнформацииПриОбращении)" в 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

В процессе разработки или отладки в 1С часто возникают ситуации, когда мы сталкиваемся с ошибкой "Значение не является значением объектного типа". Эта ошибка указывает на то, что в определенный момент выполнения кода мы пытаемся обратиться к свойству или методу переменной, которая не содержит ссылку на объект, а имеет значение типа Неопределено, Null или пустую ссылку. Или же, что менее очевидно, переменная содержит значение примитивного типа (например, строка или число), но мы пытаемся работать с ней как с объектом.

В этой статье мы подробно разберем, как выявить и устранить причину такой ошибки, используя реальный пример из форума и обобщая лучшие практики.

Разбираем исходную проблему

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


Об = Выборка.ДокументОснование;
БИТ = Об.CRM_Основание.ИсточникИнформацииПриОбращении;
Сообщить(БИТ);

При выполнении этого кода система выдала следующую ошибку:


{Обработка.ОтчетПоОплате.Форма.Форма.Форма(533)}: Значение не является значением объектного типа (ИсточникИнформацииПриОбращении) БИТ= Выборка.ДокументОснование.CRM_Основание.ИсточникИнформацииПриОбращении;

Эта ошибка говорит нам, что выражение, стоящее непосредственно перед .ИсточникИнформацииПриОбращении, а именно Об.CRM_Основание, на момент выполнения не является значением объектного типа. Это означает, что Об.CRM_Основание могло быть Неопределено, пустой ссылкой, или даже значением примитивного типа (например, строка), когда код пытался получить его свойство ИсточникИнформацииПриОбращении.

Шаги по диагностике и основные причины

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

  1. Незаполненные ссылки: Одна из частей цепочки ссылок (например, ДокументОснование или CRM_Основание) не заполнена в базе данных.
  2. Значение Неопределено: Переменная или промежуточное звено в цепочке не было инициализировано или явно присвоено Неопределено.
  3. Удаленные объекты: Ссылка указывает на объект, который был удален из информационной базы, делая ссылку "битой".
  4. Неверный тип данных: Поле в метаданных имеет тип, отличный от того, который ожидает код (например, строка вместо ссылки). Это оказалось ключевым в нашем случае.
  5. Программное обнуление: В коде произошло явное присвоение Неопределено переменной, которая должна была быть объектом.

Для начала отладки всегда рекомендуется использовать отладчик 1С. Поставив точку останова на строке с ошибкой и проанализировав значения переменных Выборка.ДокументОснование и Об.CRM_Основание, мы сможем увидеть их текущий тип и значение. Это поможет нам точно понять, на каком этапе цепочки ссылка перестает быть объектом.

Решение 1: Каскадные проверки на заполненность и тип значения

Одним из самых распространенных подходов к предотвращению этой ошибки является последовательная проверка каждого звена в цепочке ссылок на заполненность или на тип значения, прежде чем обращаться к его реквизитам.

Проверки с использованием Пустая() и ЗначениеЗаполнено()

Сначала на форуме были предложены варианты с использованием методов Пустая() и функции ЗначениеЗаполнено():

Вариант 1 (проверка только последнего звена):


Если НЕ Об.CRM_Основание.Пустая() Тогда
    БИТ = Об.CRM_Основание.ИсточникИнформацииПриОбращении;
    Сообщить(БИТ);
КонецЕсли;

Этот вариант не помог автору, потому что если Об.CRM_Основание само по себе является Неопределено (а не пустой ссылкой), то попытка вызвать метод .Пустая() на Неопределено снова вызовет ошибку "Значение не является значением объектного типа", но уже на CRM_Основание.

Вариант 2 (вложенные проверки):


Если НЕ Выборка.ДокументОснование.Пустая() Тогда
    Об = Выборка.ДокументОснование;
    Если НЕ Об.CRM_Основание.Пустая() Тогда
        БИТ = Об.CRM_Основание.ИсточникИнформацииПриОбращении;
        Сообщить(БИТ);
    КонецЕсли;
КонецЕсли;

И этот вариант также не устранил проблему для автора. Это свидетельствует о том, что либо Об.CRM_Основание все еще оставалось Неопределено после первой проверки, либо проблема крылась глубже в типе данных, как мы увидим позднее.

Вариант 3 (с использованием ЗначениеЗаполнено()):


Если ЗначениеЗаполнено(Выборка.ДокументОснование) Тогда
    Об = Выборка.ДокументОснование;
    Если ЗначениеЗаполнено(Об.CRM_Основание) Тогда // В исходном сообщении была опечатка ЗначениеЗаполненоОб.CRM_Основание)
        БИТ = Об.CRM_Основание.ИсточникИнформацииПриОбращении;
        Сообщить(БИТ);
    КонецЕсли;
КонецЕсли;

Функция ЗначениеЗаполнено() более универсальна, чем .Пустая(), так как она корректно работает как с пустыми ссылками, так и со значением Неопределено (возвращая Ложь в обоих случаях). Однако, и этот подход не помог автору, что может указывать на то, что проблема была не в пустой ссылке или Неопределено, а в том, что Об.CRM_Основание было *действительным* значением, но не того типа, от которого можно получить реквизит ИсточникИнформацииПриОбращении. Например, если Об.CRM_Основание было строкой.

Более надежные каскадные проверки с использованием ТипЗнч()

Чтобы избежать ошибки, когда мы пытаемся получить свойство от Неопределено, нам нужно сначала убедиться, что переменная не является Неопределено. Для этого идеально подходит функция ТипЗнч(). Мы можем строить каскадные проверки следующим образом:


// Проверяем, что Выборка.ДокументОснование не Неопределено
Если ТипЗнч(Выборка.ДокументОснование) <> Тип("Неопределено") Тогда
    Об = Выборка.ДокументОснование;

    // Теперь, когда Об гарантированно не Неопределено, можем проверить Об.CRM_Основание
    // Сначала убедимся, что Об.CRM_Основание не является Неопределено
    Если ТипЗнч(Об.CRM_Основание) <> Тип("Неопределено") Тогда
        // Если Об.CRM_Основание - это ссылка, то проверим, что она заполнена
        Если Об.CRM_Основание <> Справочники.ИмяСправочникаCRMОснований.ПустаяСсылка() И
             Об.CRM_Основание <> Документы.ИмяДокументаCRMОснований.ПустаяСсылка() Тогда // Добавьте все возможные типы ссылок
             // Также можно использовать ЗначениеЗаполнено() для универсальной проверки ссылок
             // Если ЗначениеЗаполнено(Об.CRM_Основание) Тогда
                // Теперь мы уверены, что Об.CRM_Основание является заполненным объектом
                // и от него можно попытаться получить реквизит ИсточникИнформацииПриОбращении
                Попытка
                    БИТ = Об.CRM_Основание.ИсточникИнформацииПриОбращении;
                    Сообщить(БИТ);
                Исключение
                    // Здесь может быть ошибка, если у объекта Об.CRM_Основание нет реквизита ИсточникИнформацииПриОбращении
                    // или ИсточникИнформацииПриОбращении сам оказался не объектного типа
                    Сообщить("Ошибка при доступе к ИсточникИнформацииПриОбращении: " + ОписаниеОшибки());
                КонецПопытки;
            // КонецЕсли; // Если использовали ЗначениеЗаполнено
        КонецЕсли;
    КонецЕсли;
КонецЕсли;

Важный нюанс: Если Об.CRM_Основание является пустым значением другого типа, например, пустой строкой, то ТипЗнч(Об.CRM_Основание) будет Тип("Строка"), и попытка обратиться к .ИсточникИнформацииПриОбращении на строке вызовет ошибку "Значение не является значением объектного типа". В таких случаях необходимо знать ожидаемый тип данных и проверять его. Например, если ожидается ссылка на справочник:


Если ТипЗнч(Выборка.ДокументОснование) = Тип("ДокументСсылка.ИмяДокумента") Тогда // Замените на реальный тип ДокументОснование
    Об = Выборка.ДокументОснование;
    // Убедимся, что CRM_Основание является ссылкой на справочник и заполнено
    Если ТипЗнч(Об.CRM_Основание) = Тип("СправочникСсылка.ИсточникиИнформации") И ЗначениеЗаполнено(Об.CRM_Основание) Тогда // Замените на реальный тип CRM_Основание
        // Теперь безопасно получаем реквизит
        БИТ = Об.CRM_Основание.ИсточникИнформацииПриОбращении;
        Сообщить(БИТ);
    КонецЕсли;
КонецЕсли;

Такой подход делает код более устойчивым к неожиданным значениям.

Решение 2: Обработка исключений с помощью Попытка...Исключение

На форуме также был предложен вариант с использованием конструкции Попытка...Исключение:


Попытка
    Об = Выборка.ДокументОснование;
    БИТ = Об.CRM_Основание.ИсточникИнформацииПриОбращении;
    Сообщить(БИТ);
Исключение
    // Здесь можно вывести сообщение об ошибке в журнал регистрации или проигнорировать ее
    Сообщить("Произошла ошибка при получении ИсточникаИнформацииПриОбращении: " + ОписаниеОшибки());
КонецПопытки;

Этот подход позволяет перехватывать и обрабатывать возникающие ошибки, предотвращая аварийное завершение работы программы. Однако мы должны понимать, что Попытка...Исключение не решает корневую причину проблемы, а лишь "обходит" ее. Мы скрываем ошибку, но не устраняем ее. Использовать этот метод стоит только тогда, когда мы точно знаем, что отсутствие значения или невозможность его получения не является критичной проблемой, и ошибку можно просто залогировать или проигнорировать.

Решение 3: Получение данных непосредственно в запросе

Один из наиболее элегантных и производительных способов решения подобных проблем — это получение всех необходимых данных непосредственно в запросе. Запрос в 1С гораздо эффективнее работает с данными на стороне СУБД, и он может обрабатывать незаполненные ссылки, возвращая NULL или пустые значения, которые затем легче обрабатывать в коде.

Вместо того чтобы получать ДокументОснование, а затем в цикле обходить его свойства, мы можем "протянуть" необходимые реквизиты сразу в запрос. Например, если ДокументОснование — это ссылка на документ, а у него есть реквизит CRM_Основание, который, в свою очередь, является ссылкой на справочник, имеющий реквизит ИсточникИнформацииПриОбращении, запрос будет выглядеть примерно так:


ВЫБРАТЬ
    ДокументСсылка.Реквизит1,
    ДокументСсылка.Реквизит2,
    ДокументСсылка.ДокументОснование.CRM_Основание.ИсточникИнформацииПриОбращении КАК ИсточникИнформации
ИЗ
    Документ.ИмяДокумента КАК ДокументСсылка
ГДЕ
    // ... условия выборки ...

Если какое-либо звено в цепочке ДокументСсылка.ДокументОснование.CRM_Основание.ИсточникИнформацииПриОбращении окажется незаполненным, запрос вернет NULL для поля ИсточникИнформации. В коде мы затем можем легко проверить это значение:


// После выполнения запроса и получения Выборки
Пока Выборка.Следующий() Цикл
    Если Выборка.ИсточникИнформации = NULL Тогда
        // Обработка случая, когда источник информации отсутствует
        Сообщить("Источник информации для документа " + Выборка.ДокументСсылка + " не заполнен.");
    Иначе
        Сообщить("Источник информации: " + Выборка.ИсточникИнформации);
    КонецЕсли;
КонецЦикла;

Этот подход более надежен и производителен, поскольку все проверки на существование ссылок и получение реквизитов выполняются на уровне базы данных, а не в коде 1С, что минимизирует вероятность ошибки "Значение не является значением объектного типа".

Ключевое решение: Проверка и корректировка структуры метаданных

Самое интересное в данной теме на форуме — это окончательное решение, найденное автором:

"Проблема была в следующем поле с типом справочник ссылка сделал текстовым ."

Это ключевая информация, которая указывает на более глубокую причину ошибки. Если поле ИсточникИнформацииПриОбращении (или даже CRM_Основание, на которое мы ссылаемся) в метаданных конфигурации имело тип "СправочникСсылка", но по логике должно было хранить текстовую информацию, то попытка обратиться к нему как к объекту (т.е. получить его реквизит ИсточникИнформацииПриОбращении) всегда будет приводить к ошибке, если оно фактически содержит не ссылку или не тот объект, который ожидается.

Разберем это подробнее:

  1. Несоответствие типа поля: Возможно, поле CRM_Основание в документе ДокументОснование изначально было создано как "СправочникСсылка", но в действительности в нем хранились строки (например, в результате неправильного импорта или ручного ввода). Тогда, когда код получал Об.CRM_Основание, он получал строку. Попытка вызвать у строки свойство .ИсточникИнформацииПриОбращении приведет к ошибке "Значение не является значением объектного типа", потому что строка не является объектом, у которого есть такой реквизит.
  2. Неправильное понимание структуры: Возможно, реквизит ИсточникИнформацииПриОбращении не является свойством объекта, на который ссылается Об.CRM_Основание, а сам Об.CRM_Основание (или Выборка.ДокументОснование.CRM_Основание) должен был содержать уже готовое текстовое значение. Изначально это поле было ссылочным, но требовалось получить его представление как текст, а не реквизит вложенного объекта.

Изменение типа поля со "СправочникСсылка" на "Строка" в метаданных и соответствующая корректировка кода, который теперь просто обращается к значению поля как к строке, решает проблему. Это подчеркивает важность того, чтобы описание метаданных (типы полей) полностью соответствовало тому, как с ними работает ваш код и какие данные в них хранятся.

Мы должны всегда сверять следующие аспекты:

Итоговые рекомендации

Для создания надежного и безошибочного кода в 1С, особенно при работе со сложными цепочками ссылок, мы рекомендуем следующий комплексный подход:

  1. Всегда используйте отладчик: Это ваш лучший инструмент для понимания текущего состояния переменных и точного места возникновения ошибки.
  2. Предпочитайте получение данных в запросе: Если это возможно, старайтесь получать необходимые данные, включая "глубокие" реквизиты, непосредственно в запросе. Это повышает производительность и упрощает обработку NULL-значений.
  3. Используйте каскадные проверки: Если работа с данными возможна только в коде (например, в обработке событий), стройте проверки каждого звена цепочки. Начинайте с проверки на Неопределено с помощью ТипЗнч(), затем используйте ЗначениеЗаполнено() для ссылочных типов.
  4. Проверяйте и синхронизируйте метаданные: Убедитесь, что типы данных реквизитов в конфигураторе соответствуют вашей логике и ожидаемым данным. Если поле должно быть строкой, сделайте его строкой; если ссылкой — ссылкой.
  5. Попытка...Исключение — это крайняя мера: Используйте обработку исключений только тогда, когда вы сознательно хотите проигнорировать или залогировать некритичную ошибку, а не решить ее.

Следуя этим рекомендациям, мы сможем значительно снизить вероятность возникновения ошибки "Значение не является значением объектного типа" и создавать более стабильные и предсказуемые решения в 1С.

← На главную