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

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

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

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

Зачем это нужно?

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

Решение для ссылочных типов (Справочники, Документы, Планы видов характеристик)

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

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

Пример кода для модуля менеджера справочника или документа:


// Определяем тип объекта через пустую ссылку
ТипОбъекта = ТипЗнч(ПустаяСсылка());

// Получаем объект метаданных по типу
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипОбъекта);

// Получаем полное имя (например, "Справочник.Номенклатура")
ПолноеИмя = ОбъектМетаданных.ПолноеИмя();

Этот метод считается эталонным для ссылочных типов, так как он не создает лишних объектов в памяти и работает максимально быстро.

Решение для регистров сведений

С регистрами сведений ситуация несколько сложнее, так как у них нет понятия «ссылки», а значит, метод ПустаяСсылка() в их модуле менеджера отсутствует. Однако у регистров сведений есть метод СоздатьКлючЗаписи(). Если вызвать его без параметров, он вернет пустой ключ записи определенного типа.

Проанализируем пример получения метаданных для регистра сведений:


// Создаем пустой ключ записи и получаем его тип
ТипКлюча = ТипЗнч(СоздатьКлючЗаписи());

// Находим метаданные по типу ключа
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипКлюча);

// Выводим результат
Сообщить(ОбъектМетаданных.ПолноеИмя());

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

Решение для отчетов и обработок

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

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


// Создаем экземпляр обработки/отчета
ОбъектЭкземпляр = Создать();

// Получаем полное имя из метаданных объекта
ИмяОбъекта = ОбъектЭкземпляр.Метаданные().ПолноеИмя();

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

Универсальный подход через анализ стека вызовов

Если нам нужно решение, которое будет работать вообще в любом модуле менеджера (включая регистры накопления, где нет пустых ссылок или простых ключей), мы можем прибегнуть к анализу стека вызовов. Это более сложный, но универсальный «хакерский» метод.

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

Посмотрим на пример реализации для современных версий платформы (8.3.10+):


Попытка
    // Генерируем исключение
    ВызватьИсключение "ОпределяемКонтекст";
Исключение
    Информация = ИнформацияОбОшибке();
    // Анализируем стек вызовов
    Для Каждого СтрокаСтека Из Информация.СтекВызовов Цикл
        // Строка обычно содержит: "Справочник.Номенклатура.МодульМенеджера : 15 : ИмяПроцедуры"
        Если Найти(СтрокаСтека.Представление, "МодульМенеджера") > 0 Тогда
            // Здесь можно применить регулярные выражения или функции работы со строками
            // для выделения части "Справочник.Номенклатура"
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецПопытки;

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

Архитектурные рекомендации

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

  1. Явная передача параметров: Если вы вызываете универсальный метод из модуля менеджера, передайте в него тип или имя метаданных явно. Это делает код прозрачным и легким для чтения.
  2. Использование кэширования: Если вы все же используете поиск через Метаданные.НайтиПоТипу(), результат определения стоит сохранить в повторно используемое значение или переменную модуля (если это допустимо контекстом), чтобы не нагружать сервер повторными поисками.
  3. Проверка контекста исполнения: Помните, что модуль менеджера может исполняться на стороне сервера. Убедитесь, что используемые вами методы (например, работа с метаданными) доступны в текущем контексте исполнения.

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

← На главную