При работе с платформой 1С:Предприятие разработчики часто сталкиваются с объектом ОписаниеТипов. Казалось бы, это простой контейнер для перечня доступных типов, но у него есть скрытые механизмы работы, особенно когда речь заходит о значении Неопределено. Разберем классическую задачу с собеседования, которая ставит в тупик даже опытных специалистов, и выясним, как платформа управляет составом типов на самом деле.
Рассмотрим программный код, который часто приводится в качестве каверзного вопроса:
можетСсылка = Неопределено;
типВсеСсылки = Справочники.ТипВсеСсылки();
ок1 = типВсеСсылки.СодержитТип(ТипЗнч(можетСсылка));
ок2 = типВсеСсылки.Типы().Найти(ТипЗнч(можетСсылка)) <> Неопределено;
Вопрос заключается в том, чему будут равны переменные ок1 и ок2. На первый взгляд логика подсказывает, что результаты должны быть одинаковыми, однако правильный ответ: ок1 = Истина, а ок2 = Ложь. Давайте разберем по шагам, почему это происходит и какая внутренняя логика платформы за этим стоит.
Ключ к пониманию кроется в том, как 1С трактует составные типы. Согласно документации и методологическим рекомендациям, если объект ОписаниеТипов содержит более одного типа, то он автоматически и неявно допускает хранение значения Неопределено.
Это необходимо для обеспечения работы реквизитов объектов. Если реквизит имеет составной тип, то его значением по умолчанию всегда будет Неопределено. Платформа проектировалась таким образом, чтобы проверка на допустимость типа (метод СодержитТип()) подтверждала возможность записи пустого значения (Неопределено) в такое поле.
Проанализируем ситуацию с методом Справочники.ТипВсеСсылки(). В любой типовой конфигурации количество справочников исчисляется десятками или сотнями. Следовательно, возвращаемый объект ОписаниеТипов гарантированно является составным. Именно поэтому:
СодержитТип(Тип("Неопределено")) возвращает Истина, так как объект «понимает», что в поле такого типа может быть записано Неопределено.Типы() возвращает массив явных типов, которые были определены при создании объекта. Поскольку Неопределено не является ссылочным типом справочника, в этом массиве его нет. Поиск по массиву через Найти() закономерно возвращает Неопределено (индекс не найден), что в выражении сравнения дает Ложь.Логика работы с Неопределено напрямую влияет на поведение метода ПривестиЗначение(). Рассмотрим два сценария, чтобы лучше понять механику:
Сценарий 1: Одиночный тип
Если в ОписаниеТипов задан только один тип (например, только СправочникСсылка.Контрагенты), то Неопределено не входит в него неявно. При попытке выполнить ПривестиЗначение(Неопределено) система вернет пустую ссылку контрагента, а не Неопределено.
Сценарий 2: Составной тип
Если в ОписаниеТипов добавлены СправочникСсылка.Контрагенты и СправочникСсылка.Номенклатура, то вызов ПривестиЗначение(Неопределено) вернет именно Неопределено, так как это значение теперь считается валидным для данного набора типов.
Проанализируем современные реалии платформы, начиная с версии 8.3.20. Ранее методы вроде Справочники.ТипВсеСсылки() возвращали типы только из основной конфигурации. Это создавало проблемы при попытке использовать объекты из расширений в универсальных механизмах.
В актуальных версиях платформы результат ТипВсеСсылки().Типы() включает в себя типы справочников как основной конфигурации, так и всех подключенных расширений. Это важно учитывать при отладке кода в конфигурациях с активным использованием расширений, так как состав типов может меняться динамически в зависимости от состава подключенных модулей.
С точки зрения оптимизации кода крайне важно использовать метод СодержитТип(). Разберем причины:
Типы() при каждом вызове создает новый объект Массив и копирует в него ссылки на типы. Если вы делаете это внутри цикла (например, проверяя типы в таблице на 10 000 строк), это приведет к избыточной нагрузке на сборщик мусора и замедлению работы.СодержитТип() реализован на уровне платформы с использованием эффективных алгоритмов поиска (вероятно, хэш-таблиц), в то время как поиск по массиву является линейным.Важно не путать Неопределено с NULL. В объекте ОписаниеТипов тип NULL ведет себя иначе. Его нельзя добавить туда вручную. Он появляется там автоматически, например, когда ОписаниеТипов создается на основе колонок таблицы значений, полученной из результата запроса с левым соединением.
Интересно, что для колонок запроса метод СодержитТип(Тип("NULL")) может возвращать Истина, но при этом в массиве Типы() значение NULL также будет отсутствовать. Это еще раз подтверждает, что ОписаниеТипов — это более сложный объект, чем просто обертка над массивом.
Выясним, как применять эти знания в реальной работе:
ОписаниеТипов.СодержитТип() для проверки валидности данных.Неопределено, а для одиночного — значение по умолчанию этого типа (0 для чисел, пустая ссылка для справочников).ОписаниеТипов конструктором без параметров Новое ОписаниеТипов(), то такой объект считается описывающим «Любой тип». В этом случае СодержитТип() будет возвращать Истина вообще для любого переданного типа.Таким образом, понимание внутренней механики ОписаниеТипов помогает не только успешно проходить собеседования, но и избегать трудноуловимых ошибок при разработке сложных интеграционных механизмов и универсальных подсистем.