При разработке универсальных обработок в 1С:Предприятии 7.7 (например, выгрузок структуры данных, поиска ссылок или инвентаризации конфигурации) часто возникает задача обойти абсолютно все прикладные объекты: константы, справочники, документы, регистры, перечисления и другие. В платформе 1С 8.x для этого достаточно перебрать коллекцию Метаданные в цикле Для Каждого, однако в версии 7.7 объект Метаданные устроен принципиально иначе.
Разберем архитектурные ограничения платформы 1С 7.7, выясним причину затруднений и рассмотрим проверенные способы реализации универсального цикла обхода конфигурации.
Проанализируем, почему стандартный подход с обращением по индексу вида Метаданные[Индекс] не работает в платформе 7.7:
Метаданные не является коллекцией значений. Для каждого типа прикладных объектов конфигурации реализован строго отдельный метод. Причем имя этого метода задается в единственном числе: Метаданные.Константа(), Метаданные.Справочник(), Метаданные.Документ(), Метаданные.Регистр(), Метаданные.Перечисление().Вычислить() или Выполнить(), которые позволяют исполнить произвольный фрагмент кода из строковой переменной.Реквизит(), у документов — РеквизитШапки() и РеквизитТабличнойЧасти(), а у регистров — Измерение() и Ресурс().Стандартный путь «в лоб» требует написания десятка отдельных циклов под каждый тип объектов метаданных. Однако обойти эти ограничения и объединить перебор в компактный алгоритм возможно.
Основным штатным механизмом динамического вычисления выражений в 1С 7.7 выступает системная функция Шаблон(). Если поместить выражение в квадратные скобки внутри строки, платформа попытается вычислить его и вернуть строковое представление результата.
Посмотрим на пример формирования списка типов метаданных и динамического вычисления количества объектов каждого вида:
СписокТипов = СоздатьОбъект("СписокЗначений");
СписокТипов.ДобавитьЗначение("Константа");
СписокТипов.ДобавитьЗначение("Справочник");
СписокТипов.ДобавитьЗначение("Документ");
СписокТипов.ДобавитьЗначение("Перечисление");
СписокТипов.ДобавитьЗначение("Отчет");
СписокТипов.ДобавитьЗначение("Обработка");
СписокТипов.ДобавитьЗначение("ПланСчетов");
СписокТипов.ДобавитьЗначение("Регистр");
СписокТипов.ДобавитьЗначение("Журнал");
Для Счетчик = 1 По СписокТипов.РазмерСписка() Цикл
ИмяТипа = СписокТипов.ПолучитьЗначение(Счетчик);
// Вызов без параметров возвращает общее количество объектов данного типа
КолвоСтр = Шаблон("[Метаданные." + ИмяТипа + "()]");
КолвоОбъектов = Число(КолвоСтр);
Сообщить("Тип метаданных: " + ИмяТипа + " — всего: " + КолвоОбъектов);
КонецЦикла;
Важный нюанс: Имена методов должны передаваться строго в единственном числе (Константа, а не Константы; Справочник, а не Справочники). При передаче имени во множественном числе платформа выдаст ошибку Поле агрегатного объекта не обнаружено.
При использовании функции Шаблон() возникает сложность: она всегда возвращает строковый тип данных. Если вызвать Шаблон("[Метаданные.Справочник(1)]"), мы получим строковое представление первого справочника, но не ссылку на сам контекст объекта метаданных, из-за чего обратиться к его свойствам (например, Идентификатор, Синоним, Комментарий) будет невозможно.
Разберем красивый прием обхода этого ограничения. Добавим в глобальный модуль или в модуль текущей обработки простую процедуру-посредник:
Функция глПрисвоить(Куда, Что) Экспорт
Куда = Что;
Возврат "";
КонецФункции
Теперь мы можем передать переменную по ссылке внутрь вычисляемого шаблона. Выражение выполнит присвоение, а переменная получит реальный объект метаданных. Посмотрим, как выглядит полный универсальный перебор всех объектов конфигурации:
Процедура ОбойтиВсеМетаданные()
Перем СписокТипов, ИмяТипа, КолвоОбъектов, ТекОбъект, Номер;
СписокТипов = СоздатьОбъект("СписокЗначений");
СписокТипов.ДобавитьЗначение("Константа");
СписокТипов.ДобавитьЗначение("Справочник");
СписокТипов.ДобавитьЗначение("Документ");
СписокТипов.ДобавитьЗначение("Перечисление");
СписокТипов.ДобавитьЗначение("Отчет");
СписокТипов.ДобавитьЗначение("Обработка");
СписокТипов.ДобавитьЗначение("Регистр");
Для Счетчик = 1 По СписокТипов.РазмерСписка() Цикл
ИмяТипа = СписокТипов.ПолучитьЗначение(Счетчик);
КолвоОбъектов = Число(Шаблон("[Метаданные." + ИмяТипа + "()]"));
Если КолвоОбъектов = 0 Тогда
Продолжить;
КонецЕсли;
Сообщить("--- " + ИмяТипа + " ---");
Для Номер = 1 По КолвоОбъектов Цикл
// Помещаем ссылку на объект метаданных в переменную ТекОбъект
Шаблон("[глПрисвоить(ТекОбъект, Метаданные." + ИмяТипа + "(" + Номер + "))]");
// Теперь ТекОбъект содержит полноценный контекст метаданных
Сообщить(" " + ТекОбъект.Идентификатор + " (" + ТекОбъект.Синоним + ")");
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Если требуется получить исчерпывающую информацию о структуре объектов без ограничений встроенного языка, рассмотрим альтернативный подход — чтение системного словаря базы данных.
В каталоге любой информационной базы 1С 7.7 находится файл описания структуры таблиц:
1cv7.dd — для файловых баз формата DBF;1cv7.dds — для баз на платформе MS SQL Server.Этот файл представляет собой обычный текстовый документ с четко структурированным описанием всех констант, справочников, документов, регистров и их реквизитов. С помощью базового объекта СоздатьОбъект("Текст") можно прочитать файл и извлечь названия объектов и физических полей в одном последовательном цикле.
Еще один вариант динамического обращения к объектам — задействовать стандартный системный COM-объект операционной системы Windows MSScriptControl.ScriptControl. Он позволяет интерпретировать код на VBScript или JScript с прямым доступом к переданному объекту метаданных 1С.
Разберем пример инициализации скриптового движка:
Скрипт = СоздатьОбъект("MSScriptControl.ScriptControl");
Скрипт.Language = "VBScript";
Скрипт.AddObject("Метаданные", Метаданные);
// Получаем количество документов динамически через VBScript
КолвоДокументов = Скрипт.Eval("Метаданные.Документ()");
Сообщить("Всего документов: " + КолвоДокументов);
С помощью вызова Скрипт.Eval(...) легко сформировать любую строку обращения к методам Метаданные, получая при этом точный тип возвращаемого значения без промежуточных строковых преобразований функции Шаблон().
Шаблон() и глобальной функции глПрисвоить().Шаблон() работает медленнее прямого обращения к методам, поэтому для критичных к производительности участков кода имеет смысл оставить явные циклы.