При разработке универсальных механизмов, подсистем обмена данными или обработчиков событий часто возникает задача выяснить точный тип переданного объекта. В платформе 1С для ссылочных типов существуют удобные обобщенные типы и методы проверки (например, ЛюбаяСсылка). Однако с прикладными объектами манипулирования записями ситуация обстоит иначе. Если попытаться вызвать конструкцию вида Тип("РегистрСведенийМенеджерЗаписи"), платформа вернет Неопределено, так как абстрактного типа менеджера записи без указания конкретного регистра в системе не существует.
Разберем, почему возникает эта сложность, какие ошибки часто допускают разработчики и как правильно и производительно реализовать проверку на принадлежность к объекту РегистрСведенийМенеджерЗаписи.
Проанализируем ключевые ограничения платформы «1С:Предприятие», влияющие на проверку типа менеджера записи:
РегистрСведенийМенеджерЗаписи.КурсыВалют. Обобщенного системного типа верхнего уровня для таких объектов нет.РегистрСведенийМенеджерЗаписи доступен исключительно на стороне сервера, в толстом клиенте и во внешнем соединении. На тонком клиенте передача или создание этого объекта вызовет исключение.СтрНачинаетсяС(Строка(ТипЗнч(ОбъектДанных)), "Регистр сведений менеджер записи") содержит скрытую уязвимость. Функция Строка(ТипЗнч(...)) формирует представление на языке текущего сеанса. В англоязычном интерфейсе или при вызове из веб-сервиса с английской локалью строка примет вид InformationRegisterRecordManager.ИмяРегистра, и условие вернет ложный результат. Кроме того, менеджер записи не сериализуется в XDTO, поэтому метод XMLТипЗнч() для него возвращает Неопределено.Выясним, как проверить тип надежно с использованием дерева метаданных конфигурации. Алгоритм состоит из двух шагов: сначала мы определяем сам объект метаданных по типу переданного значения через Метаданные.НайтиПоТипу(), убеждаемся, что это именно регистр сведений, а затем сверяем точный тип через динамически сформированную строку.
Посмотрим на пример универсальной функции:
Функция ЭтоМенеджерЗаписиРегистраСведений(ЭлементДанных) Экспорт
ТипЗначения = ТипЗнч(ЭлементДанных);
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗначения);
Если ОбъектМетаданных <> Неопределено
И Метаданные.РегистрыСведений.Содержит(ОбъектМетаданных) Тогда
Возврат ТипЗначения = Тип("РегистрСведенийМенеджерЗаписи." + ОбъектМетаданных.Имя);
КонецЕсли;
Возврат Ложь;
КонецФункции
Разберем подробнее, почему обязательна вторая проверка ТипЗначения = Тип(...): метод Метаданные.НайтиПоТипу() вернет один и тот же объект метаданных регистра не только для менеджера записи, но и для набора записей (РегистрСведенийНаборЗаписей), ключа записи (РегистрСведенийКлючЗаписи) и строки набора (РегистрСведенийЗапись). Сравнение с точным типом отсекает все смежные объекты, оставляя только менеджер записи.
Если функция проверки вызывается массово (например, при обходе больших массивов данных или в универсальных обработчиках подписок на события), постоянный вызов Метаданные.НайтиПоТипу() создает лишнюю нагрузку. В этом случае эффективнее один раз собрать типы всех менеджеров записей конфигурации и проверять вхождение через платформенный объект ОписаниеТипов или кэшированное соответствие.
Рассмотрим реализацию инициализации кэша и проверки:
// Функция инициализации кэша (рекомендуется размещать в общем модуле с повторным использованием)
Функция ПолучитьКэшТиповМенеджеровЗаписи() Экспорт
МассивТипов = Новый Массив;
Для Каждого МДРегистр Из Метаданные.РегистрыСведений Цикл
// Менеджер записи существует только у независимых регистров
Если МДРегистр.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистраСведений.Независимый Тогда
МассивТипов.Добавить(Тип("РегистрСведенийМенеджерЗаписи." + МДРегистр.Имя));
КонецЕсли;
КонецЦикла;
Возврат Новый ОписаниеТипов(МассивТипов);
КонецФункции
// Быстрая процедура проверки
Функция ЭтоМенеджерЗаписи(ЭлементДанных) Экспорт
КэшТипов = ПолучитьКэшТиповМенеджеровЗаписи();
Возврат КэшТипов.СодержитТип(ТипЗнч(ЭлементДанных));
КонецФункции
Преимущества такого подхода: Метод СодержитТип() объекта ОписаниеТипов реализован на уровне платформы в бинарном коде и отрабатывает практически мгновенно. При этом алгоритм полностью независим от локализации сеанса пользователя и безошибочно отделяет менеджер записи от наборов записей и ключей записей.
Подведем итог: для разовых проверок отлично подходит связка Метаданные.НайтиПоТипу() и динамического сравнения типа через Тип("РегистрСведенийМенеджерЗаписи." + Имя). Для высоконагруженных сценариев правильным выбором будет предварительное кэширование списка типов в ОписаниеТипов с последующим использованием метода СодержитТип().