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

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

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

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

Выясняем причину: почему «ПустаяСсылка не равна ПустойСсылка»?

Рассмотрим классическую ситуацию. В расширение заимствован справочник Контрагенты. В коде расширения (например, в обработчике «После» или «Вместо» какого-либо метода) разработчик пишет следующее условие:


Если Контрагент = ПредопределенноеЗначение("Справочник.Контрагенты.ПустаяСсылка") Тогда
    // Данный блок кода не выполняется, даже если Контрагент пустой!
КонецЕсли;

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

  1. Выражение Контрагент возвращает пустую ссылку справочника контрагентов.
  2. Выражение ПредопределенноеЗначение("Справочник.Контрагенты.ПустаяСсылка") также возвращает пустую ссылку этого же справочника.
  3. Однако выражение сравнения Контрагент = ПредопределенноеЗначение("Справочник.Контрагенты.ПустаяСсылка") возвращает значение Ложь!

Если же мы перепишем условие, обратившись напрямую к менеджеру справочника, система отработает корректно и вернет Истина:


Если Контрагент = Справочники.Контрагенты.ПустаяСсылка() Тогда
    // Этот код успешно выполняется
КонецЕсли;

Такое поведение связано с технической ошибкой платформы (официально зарегистрированный баг фирмы «1С» под номером 000038192). Причина кроется в особенностях кэширования и сопоставления типов метаданных. Когда объект заимствуется в расширение конфигурации, платформа создает для него внутреннее описание типов в контексте этого расширения. Из-за некорректной десериализации или рассинхронизации пространств имен, функция ПредопределенноеЗначение() генерирует пустую ссылку, привязанную к контексту типов расширения, в то время как реквизит из базы данных (или переданный из основной конфигурации) ссылается на тип основной конфигурации.

Для платформы на низком бинарном уровне это два разных типа данных, несмотря на одинаковое строковое представление в отладчике. Отсюда и возникает феномен «неравенства» одинаковых пустых ссылок.

Какие объекты и версии платформы подвержены проблеме?

Данная проблема наиболее ярко проявила себя в версиях платформы линейки 8.3.11 (в частности, в релизе 8.3.11.2867), но также периодически воспроизводилась и на более поздних версиях вплоть до 8.3.14. Ошибка затрагивает следующие сценарии:

Хотя фирма «1С» заявила об исправлении этой ошибки в релизе 8.3.11.2924, разработчики отмечают аналогичные симптомы при работе с перечислениями и в более поздних версиях. Поэтому важно знать безопасные шаблоны программирования, исключающие подобные риски.

Разбираем надежные способы обхода проблемы

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

Способ 1. Использование прямого обращения к менеджеру объекта

Для получения пустой ссылки заимствованного справочника или документа всегда используйте прямое обращение к его менеджеру вместо строкового вызова ПредопределенноеЗначение(). Этот метод гарантирует получение системной ссылки в правильном контексте типов:


// Безопасный вариант сравнения
Если Контрагент = Справочники.Contragents.ПустаяСсылка() Тогда
    // Код гарантированно сработает
КонецЕсли;

Способ 2. Использование системной функции ЗначениеЗаполнено()

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


// Оптимальный и быстрый способ проверки заполнения
Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
    // Объект не заполнен (пустая ссылка, Неопределено или Null)
КонецЕсли;

Важное примечание: функция ЗначениеЗаполнено() вернет Ложь для любого пустого значения (будь то пустая ссылка справочника, пустая дата, число 0 или пустая строка). Если по логике программы вам критически важно убедиться, что в реквизите находится именно пустая ссылка конкретного типа (например, при анализе составных типов данных), используйте комбинированную проверку:


Если ТипЗнч(Контрагент) = Тип("СправочникСсылка.Контрагенты") И НЕ ЗначениеЗаполнено(Контрагент) Тогда
    // Мы точно знаем, что это пустая ссылка именно справочника Контрагенты
КонецЕсли;

Способ 3. Использование функций Библиотеки стандартных подсистем (БСП)

Если ваша конфигурация построена на базе БСП, вы можете использовать возможности общего модуля ОбщегоНазначенияКлиентСервер. Функция ПредопределенныйЭлемент() из этого модуля выполняет дополнительные проверки безопасности и корректно инициализирует объекты даже при возникновении платформенных коллизий с кэшем:


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

Способ 4. Очистка серверного кэша при внезапном проявлении бага

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

  1. Остановите службу сервера «1С:Предприятия».
  2. Очистите содержимое каталогов кэша на сервере (папки вида snccg и локальные кэши пользователей в каталоге Local Application Data\1C\1Cv8).
  3. Запустите службу сервера заново.

Это полностью обновит связи метаданных между основной конфигурацией и вашими расширениями.

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

← На главную