При разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью программного изменения данных формы на сервере. Для этого используется стандартная процедура преобразования ДанныеФормыВЗначение. Однако стандартный подход к получению второго параметра этой функции — типа объекта — часто реализуется неоптимально. Рассмотрим, почему популярные методы могут замедлять систему, и разберем наиболее эффективные способы решения этой задачи.
Разберем типичную ситуацию, представленную в коде автора. Для получения типа объекта часто используется конструкция ТипЗнч(Ссылка.Скопировать()). На первый взгляд это кажется удобным, так как метод Скопировать() возвращает готовый объект нужного типа. Но давайте проанализируем, что происходит "под капотом" платформы:
Скопировать() платформа инициирует событие ПриКопировании в модуле объекта. Если там прописана сложная логика (очистка префиксов, пересчет дат, заполнение ответственных), она будет выполнена впустую.ПолучитьОбъект()), система выполняет полноценный SQL-запрос SELECT, вычитывая все поля таблицы. Если нам нужен только Тип, это крайне нерационально.Рассмотрим код, который вызывает вопросы у разработчиков:
// Неоптимальный вариант
ДокументОбъект = ДанныеФормыВЗначение(ОбъектФорма, ТипЗнч(ОбъектФорма.Ссылка.Скопировать()));
// Чуть лучше, но все еще медленно
ДокументОбъект = ДанныеФормыВЗначение(ОбъектФорма, ТипЗнч(ОбъектФорма.Ссылка.ПолучитьОбъект()));
Наиболее правильным и производительным способом получения типа объекта является обращение к метаданным. Этот метод работает исключительно в памяти сервера и не инициирует запросы к базе данных или выполнение кода в модулях объекта. Проанализируем, как это реализовать:
// Получаем имя метаданных напрямую из ссылки
ИмяОбъекта = ОбъектФорма.Ссылка.Метаданные().Имя;
// Формируем строковое представление типа объекта
СтрокаТипа = "ДокументОбъект." + ИмяОбъекта;
// Преобразуем строку в тип и передаем в функцию
ТипОбъекта = Тип(СтрокаТипа);
ДокументОбъект = ДанныеФормыВЗначение(ОбъектФорма, ТипОбъекта);
Этот подход универсален. Если процедура должна работать не только с документами, но и со справочниками, мы можем выяснить "класс" метаданных программно. Рассмотрим более продвинутый пример:
МетаданныеОбъекта = ОбъектФорма.Ссылка.Метаданные();
ПолноеИмя = МетаданныеОбъекта.ПолноеИмя(); // Например, "Документ.РеализацияТоваровУслуг"
// Заменяем точку на "Объект." для получения типа прикладного объекта
ИмяТипаОбъекта = СтрЗаменить(ПолноеИмя, ".", "Объект.");
ДокументОбъект = ДанныеФормыВЗначение(ОбъектФорма, Тип(ИмяТипаОбъекта));
Существует еще один "хитрый" способ, который упоминался в обсуждении на форуме. Он основан на том, что строковое представление типа ссылки и типа объекта различаются лишь ключевым словом. Выясним, как это работает:
// Преобразуем тип ссылки в строку, например, "ДокументСсылка.РеализацияТоваровУслуг"
СтрокаТипаСсылки = Строка(ТипЗнч(ОбъектФорма.Ссылка));
// Заменяем "Ссылка" на "Объект"
СтрокаТипаОбъекта = СтрЗаменить(СтрокаТипаСсылки, "Ссылка.", "Объект.");
// Получаем итоговый тип
ДокументОбъект = ДанныеФормыВЗначение(ОбъектФорма, Тип(СтрокаТипаОбъекта));
Важно: Этот метод опирается на строковые представления, которые теоретически могут зависеть от локализации платформы (хотя в коде обычно используются англоязычные или канонические русские имена). Поэтому способ с использованием Метаданные().Имя считается более надежным.
Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), мы рекомендуем не изобретать велосипед, а использовать готовые инструменты общего назначения. Это сделает ваш код чище и понятнее для других разработчиков.
Проанализируем пример использования модуля ОбщегоНазначения:
// Получаем имя таблицы (например, "Документ.РеализацияТоваровУслуг")
ПолноеИмяМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоСсылке(ОбъектФорма.Ссылка).ПолноеИмя();
// Формируем тип объекта
ТипОбъекта = Тип(ПолноеИмяМетаданных + "Объект");
ДокументОбъект = ДанныеФормыВЗначение(ОбъектФорма, ТипОбъекта);
Разберем ситуацию, когда ОбъектФорма является реквизитом формы с типом ДанныеФормыСтруктура. В этом случае платформа зачастую уже обладает информацией о типе данных. Однако, если мы пишем универсальную процедуру (как в примере автора, где используется &ИзменениеИКонтроль), передача точного типа вторым параметром обязательна для корректной десериализации данных.
Посмотрим на итоговый оптимизированный вариант процедуры из примера:
&ИзменениеИКонтроль("УстановитьНомерЧекаДокументаВФорме")
Процедура МИ_УстановитьНомерЧекаДокументаВФорме(ОбъектФорма, НомерЧека, ИмяРеквизита)
Если ТипЗнч(ОбъектФорма) = Тип("ФормаКлиентскогоПриложения") Тогда
ДокументОбъект = ОбъектФорма.РеквизитФормыВЗначение("Объект");
Иначе
// Оптимизированное получение типа без Скопировать() и ПолучитьОбъект()
МетаДанныеСсылки = ОбъектФорма.Ссылка.Метаданные();
ТипОбъекта = Тип("ДокументОбъект." + МетаДанныеСсылки.Имя);
ДокументОбъект = ДанныеФормыВЗначение(ОбъектФорма, ТипОбъекта);
КонецЕсли;
ДокументОбъект[ИмяРеквизита] = НомерЧека;
ДокументОбъект.ОбменДанными.Загрузка = Истина; // Позволяет обойти лишние проверки при записи
ДокументОбъект.Записать();
Если ТипЗнч(ОбъектФорма) = Тип("ФормаКлиентскогоПриложения") Тогда
ОбъектФорма.ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецЕсли;
КонецПроцедуры
Подведем итоги нашего анализа. Использование Скопировать() для получения типа — это классическая антипаттерн-ошибка, которая может привести к трудноуловимым багам из-за срабатывания логики копирования. Всегда отдавайте предпочтение работе с метаданными.
Основные преимущества использования Метаданные().Имя:
Помните, что в высоконагруженных системах даже такие мелочи, как лишнее создание объекта в памяти, могут суммироваться и приводить к общей деградации производительности. Применяя описанные подходы, вы делаете свой код более профессиональным и устойчивым к нагрузкам.