Как обратиться к реквизиту объекта 1С, не зная его имени заранее? Разбор динамической работы с реквизитами

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

Приветствуем вас, коллеги! В повседневной практике разработки на платформе 1С:Предприятие 8 часто возникает задача динамического управления данными. Представьте ситуацию: вы создаете универсальную обработку импорта данных, пишете гибкий механизм заполнения справочников или разрабатываете правила обмена. Во всех этих случаях имена реквизитов становятся известны только в момент выполнения кода (в рантайме).

Как же обратиться к реквизиту объекта, если его имя хранится в строковой переменной? Разберем эту задачу по шагам, проанализируем возможные подходы, изучим подводные камни производительности и рассмотрим лучшие практики разработки.

Базовое решение: Использование квадратных скобок

Начнем с самого простого и лаконичного способа, встроенного в саму платформу 1С. Для динамического обращения к свойствам и реквизитам объектов используется стандартный синтаксис квадратных скобок [].

Если у нас есть объект справочника (например, типа СправочникОбъект.Номенклатура) и строковая переменная, содержащая имя реквизита, мы можем напрямую прочитать или записать значение.

Рассмотрим конкретный пример:


// Создаем новый элемент справочника динамически
ИмяСправочника = "Номенклатура";
НовыйЭлемент = Справочники[ИмяСправочника].СоздатьЭлемент();

// Задаем имя реквизита в виде строки
ИмяРеквизита = "Артикул";
ЗначениеРеквизита = "ART-100234";

// Записываем значение динамически
НовыйЭлемент[ИмяРеквизита] = ЗначениеРеквизита;

// Заполняем наименование
НовыйЭлемент["Наименование"] = "Кабель силовой медный";

// Сохраняем элемент в базе данных
НовыйЭлемент.Записать();

Этот синтаксис абсолютно легитимен, работает невероятно быстро и поддерживается платформой, начиная с самых ранних версий 1С 8.0.

Ловушка типов: Ошибка «Поле объекта не обнаружено» при использовании объектов метаданных

Часто начинающие разработчики сталкиваются с ситуацией, когда конструкция Элемент[Реквизит] вызывает ошибку компиляции или выполнения. Выясним причину этой проблемы.

Ошибку легко спровоцировать, если переменная Реквизит хранит не текстовую строку, а сам объект метаданных (тип ОбъектМетаданных). Такое случается, если мы обходим метаданные справочника циклом:


// Пример кода, приводящего к ошибке
Для Каждого Реквизит Из НовыйЭлемент.Метаданные().Реквизиты Цикл
    // Попытка прямого обращения вызовет сбой, так как Реквизит — это ОбъектМетаданных, а не Строка!
    // НовыйЭлемент[Реквизит] = "Тест"; // Ошибка!
КонецЦикла;

Чтобы избежать этой ловушки, всегда проверяйте тип переменной. Нам нужно обращаться именно к строковому имени свойства, которое хранится в свойстве Имя объекта метаданных. Посмотрим на правильный вариант:


// Правильный обход и заполнение
Для Каждого Реквизит Из НовыйЭлемент.Метаданные().Реквизиты Цикл
    ИмяПоля = Реквизит.Имя;
    НовыйЭлемент[ИмяПоля] = "Какое-то значение"; 
КонецЦикла;

Различие между стандартными и пользовательскими реквизитами

Проанализируем структуру метаданных. Коллекция Метаданные().Реквизиты содержит только те реквизиты, которые были добавлены разработчиком вручную (например, Артикул, Штрихкод, Производитель).

Однако у любого справочника есть предопределенные системные поля — так называемые стандартные реквизиты (Код, Наименование, Родитель, Владелец, ПометкаУдаления и т.д.). Они хранятся в отдельной коллекции: Метаданные().СтандартныеРеквизиты.

Если перед нами стоит задача написать полностью универсальный обход абсолютно всех полей объекта, нам потребуется объединить эти коллекции. Рассмотрим, как это сделать:


// Функция для получения списка всех доступных имен полей объекта
Функция ПолучитьВсеИменаПолей(Объект1С)
    МассивИмен = Новый Массив;
    МетаданныеОбъекта = Объект1С.Метаданные();
    
    // 1. Добавляем стандартные реквизиты
    Для Каждого СтандартныйРеквизит Из МетаданныеОбъекта.СтандартныеРеквизиты Цикл
        МассивИмен.Добавить(СтандартныйРеквизит.Имя);
    КонецЦикла;
    
    // 2. Добавляем пользовательские реквизиты
    Для Каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
        МассивИмен.Добавить(Реквизит.Имя);
    КонецЦикла;
    
    Возврат МассивИмен;
КонецФункции

Обеспечение безопасности: Проверка существования реквизита перед записью

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

Рекомендуем использовать следующую безопасную обертку:


Процедура БезопасноЗаписатьРеквизит(Объект, ИмяРеквизита, Значение)
    МетаданныеОбъекта = Объект.Метаданные();
    
    // Проверяем наличие в обычных реквизитах
    ЕстьРеквизит = (МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено);
    
    // Если не нашли, ищем среди стандартных реквизитов
    Если Не ЕстьРеквизит Тогда
        ЕстьРеквизит = (МетаданныеОбъекта.СтандартныеРеквизиты.Найти(ИмяРеквизита) <> Неопределено);
    КонецЕсли;
    
    // Выполняем запись только при подтверждении существования
    Если ЕстьРеквизит Тогда
        Объект[ИмяРеквизита] = Значение;
    Иначе
        ЗаписьЖурналаРегистрации("ДинамическоеЗаполнение", 
            УровеньЖурналаРегистрации.Предупреждение, , , 
            "У объекта " + МетаданныеОбъекта.Имя + " отсутствует реквизит: " + ИмяРеквизита);
    КонецЕсли;
EndПроцедуры

Использование метода ЗаполнитьЗначенияСвойств() для пакетного заполнения

Существует еще один замечательный, крайне быстрый встроенный механизм платформы — метод ЗаполнитьЗначенияСвойств(). Он идеально подходит, когда вам нужно скопировать значения из одного источника (например, структуры данных или другого объекта) в наш создаваемый элемент.

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


// Создаем структуру с динамическими данными
ДанныеДляЗаполнения = Новый Структура;
ДанныеДляЗаполнения.Вставить("Наименование", "Новый товар");
ДанныеДляЗаполнения.Вставить("Артикул", "123-ABC");
ДанныеДляЗаполнения.Вставить("Цена", 1500);

// Наш созданный объект
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();

// Заполняем свойства объекта из структуры
ЗаполнитьЗначенияСвойств(НовыйЭлемент, ДанныеДляЗаполнения);

Платформа автоматически сопоставит ключи структуры с именами реквизитов справочника. Если какого-то реквизита в справочнике нет, платформа просто пропустит его без вызова ошибок! Это делает код чистым, лаконичным и безопасным.

Полезный лайфхак: Используйте третий и четвертый параметры метода для точечной настройки.

  1. СписокСвойств (третий параметр) — позволяет ограничить список переносимых реквизитов. Пример: ЗаполнитьЗначенияСвойств(Приемник, Источник, "Наименование, Артикул"). Будут заполнены только эти два поля.
  2. ИсключаяСвойства (четвертый параметр) — позволяет исключить определенные поля из заполнения. Крайне полезно, чтобы случайно не затереть системные поля. Пример: ЗаполнитьЗначенияСвойств(Приемник, Источник, , "Код, Ссылка").

Чтение динамических реквизитов из базы данных: Избегаем ловушек производительности

До сих пор мы говорили о заполнении создаваемого объекта (СправочникОбъект). Но что делать, если вам нужно динамически прочитать значение реквизита из уже существующего элемента, имея на руках только его ссылку (СправочникСсылка)?

Прямое обращение вида Значение = Ссылка[ИмяРеквизита] технически работает, но категорически не рекомендуется в высоконагруженных системах. При таком обращении платформа неявно считывает из СУБД абсолютно весь объект целиком со всеми его табличными частями, кэширует его и только потом отдает вам одно поле. Это сильно замедляет работу системы и перегружает оперативную память сервера.

Для быстрого и производительного чтения динамических реквизитов по ссылке следует использовать функции Библиотеки Стандартных Подсистем (БСП):


// Получение значения одного реквизита без загрузки всего объекта
ЗначениеАртикула = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаТовар, "Артикул");

// Получение нескольких реквизитов сразу (возвращает структуру)
СтруктураДанных = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаТовар, "Артикул, Код, Производитель");
Артикул = СтруктураДанных.Артикул;
Код = СтруктураДанных.Код;

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

Работа со сложными путями к реквизитам через точки

Иногда имя реквизита передается не простым словом, а составным путем через точку (например: "Контрагент.ОсновнойДоговор.ВалютаВзаиморасчетов"). Простая конструкция квадратных скобок в данном случае выдаст ошибку.

Для решения таких специфических задач можно прибегнуть к механизму динамического исполнения кода (хотя использовать его стоит с осторожностью):


// Чтение значения по сложному динамическому пути
ПутьКРеквизиту = "Контрагент.ОсновнойДоговор.ВалютаВзаиморасчетов";
ЗначениеВалюты = Вычислить("ОбъектДляПроверки." + ПутьКРеквизиту);

Внимание: Использование функций Вычислить() и Выполнить() считается "плохим тоном" (anti-pattern) в современной разработке. Данный подход усложняет отладку кода, снижает производительность и может создать бреши в безопасности, если строка пути формируется на основе непроверенных пользовательских данных. Применяйте его только тогда, когда другие методы (например, последовательный разбор строки через СтрРазделить) нецелесообразны.

Динамическая работа с табличными частями

Квадратные скобки отлично подходят и для работы с табличными частями объекта. Если имя табличной части хранится в переменной, мы можем динамически получить ее, добавить строку и заполнить колонки:


ИмяТабличнойЧасти = "Товары";
ИмяКолонкиНоменклатура = "Номенклатура";
ИмяКолонкиКоличество = "Количество";

// Получаем коллекцию строк табличной части
ТабличнаяЧасть = НовыйЭлемент[ИмяТабличнойЧасти];

// Добавляем новую строку
НоваяСтрока = ТабличнаяЧасть.Добавить();

// Заполняем колонки динамически
НоваяСтрока[ИмяКолонкиНоменклатура] = СсылкаНаТовар;
НоваяСтрока[ИмяКолонкиКоличество] = 10;

Мы подробно изучили все аспекты динамического обращения к свойствам объектов в 1С. Использование правильных методов гарантирует не только работоспособность вашего программного кода, но и высокую скорость работы всей системы в целом. Применяйте полученные знания на практике, пишите чистый и производительный код!

← На главную