Приветствуем вас, коллеги! В повседневной практике разработки на платформе 1С:Предприятие 8 часто возникает задача динамического управления данными. Представьте ситуацию: вы создаете универсальную обработку импорта данных, пишете гибкий механизм заполнения справочников или разрабатываете правила обмена. Во всех этих случаях имена реквизитов становятся известны только в момент выполнения кода (в рантайме).
Как же обратиться к реквизиту объекта, если его имя хранится в строковой переменной? Разберем эту задачу по шагам, проанализируем возможные подходы, изучим подводные камни производительности и рассмотрим лучшие практики разработки.
Начнем с самого простого и лаконичного способа, встроенного в саму платформу 1С. Для динамического обращения к свойствам и реквизитам объектов используется стандартный синтаксис квадратных скобок [].
Если у нас есть объект справочника (например, типа СправочникОбъект.Номенклатура) и строковая переменная, содержащая имя реквизита, мы можем напрямую прочитать или записать значение.
Рассмотрим конкретный пример:
// Создаем новый элемент справочника динамически
ИмяСправочника = "Номенклатура";
НовыйЭлемент = Справочники[ИмяСправочника].СоздатьЭлемент();
// Задаем имя реквизита в виде строки
ИмяРеквизита = "Артикул";
ЗначениеРеквизита = "ART-100234";
// Записываем значение динамически
НовыйЭлемент[ИмяРеквизита] = ЗначениеРеквизита;
// Заполняем наименование
НовыйЭлемент["Наименование"] = "Кабель силовой медный";
// Сохраняем элемент в базе данных
НовыйЭлемент.Записать();
Этот синтаксис абсолютно легитимен, работает невероятно быстро и поддерживается платформой, начиная с самых ранних версий 1С 8.0.
Часто начинающие разработчики сталкиваются с ситуацией, когда конструкция Элемент[Реквизит] вызывает ошибку компиляции или выполнения. Выясним причину этой проблемы.
Ошибку легко спровоцировать, если переменная Реквизит хранит не текстовую строку, а сам объект метаданных (тип ОбъектМетаданных). Такое случается, если мы обходим метаданные справочника циклом:
// Пример кода, приводящего к ошибке
Для Каждого Реквизит Из НовыйЭлемент.Метаданные().Реквизиты Цикл
// Попытка прямого обращения вызовет сбой, так как Реквизит — это ОбъектМетаданных, а не Строка!
// НовыйЭлемент[Реквизит] = "Тест"; // Ошибка!
КонецЦикла;
Чтобы избежать этой ловушки, всегда проверяйте тип переменной. Нам нужно обращаться именно к строковому имени свойства, которое хранится в свойстве Имя объекта метаданных. Посмотрим на правильный вариант:
// Правильный обход и заполнение
Для Каждого Реквизит Из НовыйЭлемент.Метаданные().Реквизиты Цикл
ИмяПоля = Реквизит.Имя;
НовыйЭлемент[ИмяПоля] = "Какое-то значение";
КонецЦикла;
Проанализируем структуру метаданных. Коллекция Метаданные().Реквизиты содержит только те реквизиты, которые были добавлены разработчиком вручную (например, Артикул, Штрихкод, Производитель).
Однако у любого справочника есть предопределенные системные поля — так называемые стандартные реквизиты (Код, Наименование, Родитель, Владелец, ПометкаУдаления и т.д.). Они хранятся в отдельной коллекции: Метаданные().СтандартныеРеквизиты.
Если перед нами стоит задача написать полностью универсальный обход абсолютно всех полей объекта, нам потребуется объединить эти коллекции. Рассмотрим, как это сделать:
// Функция для получения списка всех доступных имен полей объекта
Функция ПолучитьВсеИменаПолей(Объект1С)
МассивИмен = Новый Массив;
МетаданныеОбъекта = Объект1С.Метаданные();
// 1. Добавляем стандартные реквизиты
Для Каждого СтандартныйРеквизит Из МетаданныеОбъекта.СтандартныеРеквизиты Цикл
МассивИмен.Добавить(СтандартныйРеквизит.Имя);
КонецЦикла;
// 2. Добавляем пользовательские реквизиты
Для Каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
МассивИмен.Добавить(Реквизит.Имя);
КонецЦикла;
Возврат МассивИмен;
КонецФункции
Попытка записать данные в несуществующий реквизит гарантированно приведет к аварийному завершению работы программы с ошибкой «Поле объекта не обнаружено». Чтобы наше решение было отказоустойчивым, обязательно внедрим предварительную проверку наличия свойства в метаданных.
Рекомендуем использовать следующую безопасную обертку:
Процедура БезопасноЗаписатьРеквизит(Объект, ИмяРеквизита, Значение)
МетаданныеОбъекта = Объект.Метаданные();
// Проверяем наличие в обычных реквизитах
ЕстьРеквизит = (МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено);
// Если не нашли, ищем среди стандартных реквизитов
Если Не ЕстьРеквизит Тогда
ЕстьРеквизит = (МетаданныеОбъекта.СтандартныеРеквизиты.Найти(ИмяРеквизита) <> Неопределено);
КонецЕсли;
// Выполняем запись только при подтверждении существования
Если ЕстьРеквизит Тогда
Объект[ИмяРеквизита] = Значение;
Иначе
ЗаписьЖурналаРегистрации("ДинамическоеЗаполнение",
УровеньЖурналаРегистрации.Предупреждение, , ,
"У объекта " + МетаданныеОбъекта.Имя + " отсутствует реквизит: " + ИмяРеквизита);
КонецЕсли;
EndПроцедуры
Существует еще один замечательный, крайне быстрый встроенный механизм платформы — метод ЗаполнитьЗначенияСвойств(). Он идеально подходит, когда вам нужно скопировать значения из одного источника (например, структуры данных или другого объекта) в наш создаваемый элемент.
Давайте посмотрим на пример использования структуры для динамического заполнения:
// Создаем структуру с динамическими данными
ДанныеДляЗаполнения = Новый Структура;
ДанныеДляЗаполнения.Вставить("Наименование", "Новый товар");
ДанныеДляЗаполнения.Вставить("Артикул", "123-ABC");
ДанныеДляЗаполнения.Вставить("Цена", 1500);
// Наш созданный объект
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
// Заполняем свойства объекта из структуры
ЗаполнитьЗначенияСвойств(НовыйЭлемент, ДанныеДляЗаполнения);
Платформа автоматически сопоставит ключи структуры с именами реквизитов справочника. Если какого-то реквизита в справочнике нет, платформа просто пропустит его без вызова ошибок! Это делает код чистым, лаконичным и безопасным.
Полезный лайфхак: Используйте третий и четвертый параметры метода для точечной настройки.
ЗаполнитьЗначенияСвойств(Приемник, Источник, "Наименование, Артикул"). Будут заполнены только эти два поля.ЗаполнитьЗначенияСвойств(Приемник, Источник, , "Код, Ссылка").До сих пор мы говорили о заполнении создаваемого объекта (СправочникОбъект). Но что делать, если вам нужно динамически прочитать значение реквизита из уже существующего элемента, имея на руках только его ссылку (СправочникСсылка)?
Прямое обращение вида Значение = Ссылка[ИмяРеквизита] технически работает, но категорически не рекомендуется в высоконагруженных системах. При таком обращении платформа неявно считывает из СУБД абсолютно весь объект целиком со всеми его табличными частями, кэширует его и только потом отдает вам одно поле. Это сильно замедляет работу системы и перегружает оперативную память сервера.
Для быстрого и производительного чтения динамических реквизитов по ссылке следует использовать функции Библиотеки Стандартных Подсистем (БСП):
// Получение значения одного реквизита без загрузки всего объекта
ЗначениеАртикула = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаТовар, "Артикул");
// Получение нескольких реквизитов сразу (возвращает структуру)
СтруктураДанных = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаТовар, "Артикул, Код, Производитель");
Артикул = СтруктураДанных.Артикул;
Код = СтруктураДанных.Код;
Эти методы БСП под капотом формируют оптимальный SQL-запрос, который вытягивает из базы данных строго затребованные поля, обеспечивая максимальное быстродействие.
Иногда имя реквизита передается не простым словом, а составным путем через точку (например: "Контрагент.ОсновнойДоговор.ВалютаВзаиморасчетов"). Простая конструкция квадратных скобок в данном случае выдаст ошибку.
Для решения таких специфических задач можно прибегнуть к механизму динамического исполнения кода (хотя использовать его стоит с осторожностью):
// Чтение значения по сложному динамическому пути
ПутьКРеквизиту = "Контрагент.ОсновнойДоговор.ВалютаВзаиморасчетов";
ЗначениеВалюты = Вычислить("ОбъектДляПроверки." + ПутьКРеквизиту);
Внимание: Использование функций Вычислить() и Выполнить() считается "плохим тоном" (anti-pattern) в современной разработке. Данный подход усложняет отладку кода, снижает производительность и может создать бреши в безопасности, если строка пути формируется на основе непроверенных пользовательских данных. Применяйте его только тогда, когда другие методы (например, последовательный разбор строки через СтрРазделить) нецелесообразны.
Квадратные скобки отлично подходят и для работы с табличными частями объекта. Если имя табличной части хранится в переменной, мы можем динамически получить ее, добавить строку и заполнить колонки:
ИмяТабличнойЧасти = "Товары";
ИмяКолонкиНоменклатура = "Номенклатура";
ИмяКолонкиКоличество = "Количество";
// Получаем коллекцию строк табличной части
ТабличнаяЧасть = НовыйЭлемент[ИмяТабличнойЧасти];
// Добавляем новую строку
НоваяСтрока = ТабличнаяЧасть.Добавить();
// Заполняем колонки динамически
НоваяСтрока[ИмяКолонкиНоменклатура] = СсылкаНаТовар;
НоваяСтрока[ИмяКолонкиКоличество] = 10;
Мы подробно изучили все аспекты динамического обращения к свойствам объектов в 1С. Использование правильных методов гарантирует не только работоспособность вашего программного кода, но и высокую скорость работы всей системы в целом. Применяйте полученные знания на практике, пишите чистый и производительный код!