Каждый разработчик 1С рано или поздно сталкивается с ситуациями, которые кажутся настоящей «мистикой». Представьте картину: вы ведете пошаговую отладку кода, останавливаетесь на точке останова и видите, что переменной не присваивается возвращаемое значение функции. При этом, если скопировать эту же функцию в табло отладчика и вычислить ее вручную, она отлично отрабатывает и возвращает заполненный объект (например, структуру или соответствие). Но в самом коде переменная упорно остается в состоянии Неопределено, из-за чего на следующей строке программа аварийно завершает работу с классической ошибкой: «Значение не является значением объектного типа».
В этой статье мы подробно разберем, почему возникает этот неприятный эффект, рассмотрим физику процесса взаимодействия отладчика с платформой 1С:Предприятие 8, а также изучим надежные способы решения этой проблемы.
Рассмотрим конкретный пример из практики интеграции «ДиректБанк» в конфигурации «1С:Управление нашей фирмой» (УНФ). В общем модуле ОбменСБанкамиСлужебныйКлиент на клиенте выполняется следующий фрагмент кода:
ПараметрыВозврата = ПолучитьИзВременногоХранилища(РезультатЗадания.АдресРезультата);
Если ПараметрыВозврата.ТребуетсяТокен Тогда
// Дальнейшая обработка данных...
КонецЕсли;
При выполнении этого кода система выбрасывает исключение: Значение не является значением объектного типа (ТребуетсяТокен). Это однозначно указывает на то, что переменная ПараметрыВозврата слева от точки равна Неопределено.
Однако при анализе строки через табло отладчика выясняются удивительные вещи:
ПолучитьИзВременногоХранилища(РезультатЗадания.АдресРезультата) в табло успешно возвращает структуру, содержащую ключ ТребуетсяТокен со значением Истина или Ложь.ПараметрыВозврата при этом по-прежнему равна Неопределено.Давайте разберемся по шагам, какие скрытые механизмы платформы приводят к подобному поведению и как это исправить.
Самая частая причина «неработающего» присваивания в 1С — это так называемый сдвиг строк (рассинхронизация между исходным кодом, который вы видите на экране в Конфигураторе, и скомпилированным байт-кодом, который в данный момент исполняется на сервере или клиенте).
Такое поведение часто вызывается регулярным использованием механизма «динамического обновления» конфигурации (в обиходе — «демоническое обновление») без перезапуска рабочих процессов сервера или клиентских сеансов. Кэш метаданных локального компьютера забивается устаревшими версиями модулей.
Как это работает на уровне платформы:
Когда вы запускаете отладку, платформа сопоставляет строки исходного текста с адресами команд в байт-коде. Если кэш поврежден или устарел, синяя стрелка отладчика визуально может стоять на строке Если ПараметрыВозврата.ТребуетсяТокен Тогда, но виртуальная машина 1С фактически еще не выполнила предыдущую строку ПараметрыВозврата = ПолучитьИзВременногоХранилища(...). В ее понимании выполнение находится на шаг позади.
Именно поэтому ручное вычисление функции в табло дает корректный результат (так как кэш данных временного хранилища доступен), но сама переменная еще не получила этого значения в контексте выполнения программы.
Для устранения рассинхронизации исходного кода и исполняемого байт-кода мы можем применить несколько методик.
Метод 1. Запуск с параметром очистки кэша
Самый простой и быстрый способ для клиентского приложения — запустить сеанс «1С:Предприятие» с ключом командной строки /ClearCache. Для этого выполните следующие действия:
/ClearCache.Метод 2. Ручное удаление файлов локального кэша
Если параметрическая очистка не помогла, имеет смысл полностью удалить файлы локального кэша метаданных. Они располагаются по следующим путям на компьютере пользователя:
%USERPROFILE%\AppData\Local\1C\1cv8\%USERPROFILE%\AppData\Roaming\1C\1cv8\Перед удалением обязательно закройте все запущенные сеансы 1С:Предприятия и Конфигуратора. Удалять следует папки с длинными буквенно-цифровыми наименованиями (например, ascii-8482-4bf1...), которые представляют собой уникальные идентификаторы баз.
Метод 3. Принудительное изменение кода модуля
Если у вас нет прав на очистку кэша сервера или перезапуск служб, можно заставить платформу принудительно перекомпилировать проблемный модуль. Для этого:
Этот простой прием заставит платформу обновить скомпилированную версию модуля в кэше сервера приложений, и сдвиг строк в отладчике исчезнет.
Поскольку в рассматриваемом примере фигурирует метод ПолучитьИзВременногоХранилища(), важно проанализировать архитектурные особенности этого механизма в платформе 1С.
Временное хранилище используется для передачи данных между серверными вызовами или между фоновым заданием и клиентским приложением. Здесь разработчика могут подстерегать две скрытые проблемы:
ПолучитьИзВременногоХранилища() на клиенте вернет Неопределено, если внутри структуры во временном хранилище содержатся типы данных, которые невозможно передать с сервера на клиент. К ним относятся таблицы значений, деревья значений, запросы, COM-объекты или ссылки на прикладные объекты сервера (например, ДокументОбъект). Если сервер попытается передать такую структуру на клиент, платформа не сможет ее десериализовать, и переменная получит значение Неопределено.УникальныйИдентификатор), то данные в хранилище живут очень недолго. Они могут удалиться автоматически при первом же возврате управления с сервера на клиент или при выполнении деструктора объекта. Во время пошаговой отладки фоновые вызовы отладчика могут непреднамеренно спровоцировать очистку этих данных.Чтобы защитить программу от аварийного завершения и сделать код более устойчивым, мы рекомендуем всегда использовать защитные проверки типов перед обращением к свойствам объектов. Избегайте прямого обращения к свойствам возвращаемого значения без предварительного анализа.
Рассмотрим пример безопасного написания кода:
ПараметрыВозврата = ПолучитьИзВременногоХранилища(РезультатЗадания.АдресРезультата);
// Проверяем, что во временном хранилище действительно лежит Структура
Если ТипЗнч(ПараметрыВозврата) = Тип("Структура") Тогда
// Проверяем наличие нужного ключа в структуре
Если ПараметрыВозврата.Свойство("ТребуетсяТокен") Тогда
Если ПараметрыВозврата.ТребуетсяТокен Тогда
// Выполняем логику, если токен действительно требуется
КонецЕсли;
Иначе
ЗаписатьЛогОшибок("В полученной структуре отсутствует ключ 'ТребуетсяТокен'");
КонецЕсли;
Иначе
// Обрабатываем ситуацию, когда данные не были получены или тип не соответствует ожиданиям
ПоказатьПредупреждение(, "Не удалось получить параметры авторизации. Пожалуйста, повторите попытку.");
КонецЕсли;
Такой подход полностью исключает появление фатальной ошибки Значение не является значением объектного типа на стороне пользователя, даже если произойдет сетевой сбой или кэш данных будет временно недоступен.