Почему не работает сравнение типов объектов в расширении конфигурации 1С и как это исправить?

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

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

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

Суть проблемы: когда равенство типов дает сбой

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


флГотоваяПродукция_Продукция = ТипЗнч(ИсточникДанных) = Тип("ДокументТабличнаяЧасть.ГотоваяПродукция.Продукция");

При выполнении этого кода в основной конфигурации переменная флГотоваяПродукция_Продукция принимает значение Истина, если в качестве источника передан соответствующий объект. Однако в контексте расширения это выражение всегда возвращает Ложь, хотя отладчик показывает, что типы визуально полностью совпадают. При этом аналогичное сравнение для объектного типа (например, ДокументОбъект.ГотоваяПродукция) отрабатывает абсолютно корректно.

Выясняем причину: физика типов в платформе 1С

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

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

Когда мы заимствуем объект метаданных в расширение, платформа генерирует для него так называемые прокси-типы (или типы контекста расширения). Если объект заимствован не полностью, либо нарушена внутренняя структура связей метаданных, UUID типа в расширении начинает отличаться от UUID типа в основной конфигурации.

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


СтрокаТипа1 = ЗначениеВСтрокуВнутр(ТипЗнч(ИсточникДанных));
СтрокаТипа2 = ЗначениеВСтрокуВнутр(Тип("ДокументТабличнаяЧасть.ГотоваяПродукция.Продукция"));
// Результаты СтрокаТипа1 и СтрокаТипа2 будут содержать разные UUID!

Поскольку уникальные идентификаторы типов различаются, стандартный оператор равенства = честно возвращает нам Ложь, несмотря на то, что для пользователя (и в текстовом представлении в отладчике) эти типы выглядят одинаково.

2. Особенности заимствования составных типов и метаданных. Подобное поведение часто проявляется, когда объект метаданных заимствован в расширение частично. Например, если сам документ ГотоваяПродукция заимствован, а его табличная часть или отдельные реквизиты — нет, либо если в процессе обновления конфигурации связи между метаданными расширения и основной конфигурации «разошлись».

---

Способ 1. Сравнение через метаданные (Наиболее рекомендуемый)

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

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


// Получаем объект метаданных нашего источника данных
МетаданныеИсточника = ИсточникДанных.Метаданные();

// Проверяем имя табличной части и имя документа-родителя
флГотоваяПродукция_Продукция = (МетаданныеИсточника.Имя = "Продукция") 
    И (МетаданныеИсточника.Родитель().Имя = "ГотоваяПродукция");

Этот способ имеет несколько весомых преимуществ:

  1. Он полностью застрахован от любых багов платформы, связанных с кэшированием или генерацией прокси-типов в расширениях.
  2. Такой код работает значительно быстрее, чем строковое преобразование типов, так как обращение к свойствам метаданных в памяти платформы оптимизировано.
  3. Код остается легко читаемым и понятным для других разработчиков.

---

Способ 2. Использование функций БСП

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

Мы можем использовать функцию общего назначения для получения полного имени объекта метаданных по его типу:


// Получаем полное системное имя класса метаданных по типу значения
ПолноеИмя = ОбщегоНазначения.ИмяОбъектаМетаданных(ТипЗнч(ИсточникДанных)); 
// Результат будет: "Документ.ГотоваяПродукция.ТабличнаяЧасть.Продукция"

флГотоваяПродукция_Продукция = (ПолноеИмя = "Документ.ГотоваяПродукция.ТабличнаяЧасть.Продукция");

Использование БСП гарантирует стабильность работы решения при любых обновлениях платформы, так как разработчики БСП оперативно адаптируют свои функции под изменения в ядре 1С.

---

Способ 3. Поиск по типу в глобальной коллекции метаданных

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

Рассмотрим пример реализации:


ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(ИсточникДанных));

Если ОбъектМетаданных <> Неопределено Тогда
    флГотоваяПродукция_Продукция = (ОбъектМетаданных = Метаданные.Документы.ГотоваяПродукция.ТабличныеЧасти.Продукция);
Иначе
    флГотоваяПродукция_Продукция = Ложь;
КонецЕсли;

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

---

Способ 4. Исправление заимствования объектов в расширении

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

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

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

Довольно часто после чистого пересоздания связей типов в расширении стандартное сравнение ТипЗнч(...) = Тип(...) начинает работать без дополнительных ухищрений в коде.

---

Способ 5. Сравнение строковых представлений типов («Костыль»)

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


флГотоваяПродукция_Продукция = Строка(ТипЗнч(ИсточникДанных)) = Строка(Тип("ДокументТабличнаяЧасть.ГотоваяПродукция.Продукция"));

Приведение к строке нивелирует разницу в UUID типов, так как строковое представление (синоним типа) у них абсолютно одинаковое — "Документ табличная часть: Готовая продукция: Продукция".

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

Резюме

Мы проанализировали неочевидное поведение платформы при сравнении типов в расширении и нашли первопричину — несовпадение внутренних идентификаторов типов (UUID) из-за специфики контекста выполнения кода расширения.

Для создания отказоустойчивых и быстрых решений всегда отдавайте предпочтение сравнению через метаданные (Способ 1) или использованию инструментов БСП (Способ 2). Это сэкономит ваше время при последующих обновлениях конфигурации и обеспечит высокую скорость работы вашей программы.

← На главную