Чтение реквизитов объекта во встроенном языке 1С

· 8 мин

Содержание

Чтобы обратиться к реквизиту объекта 1С, в большинстве случаев хватает точки: Номенклатура.Артикул, Документ.Дата, СтрокаТабличнойЧасти.Цена. Приём одинаково работает и для ссылки, и для объекта, и для строки табличной части — слева имя переменной, справа имя реквизита как в конфигураторе.

Когда полей много, имя реквизита известно только во время работы или читать нужно сразу по списку объектов, точка перестаёт быть лучшим выбором. Ниже — все рабочие способы получить значение реквизита: обращение по имени через квадратные скобки, функции общего модуля из БСП, разыменование в запросе и перебор через метаданные. С примерами кода и ценой каждого способа по скорости.

Обращение через точку: разыменование и его цена

Точка — это разыменование (dereferencing). У объекта, который уже загружен в память, реквизит читается мгновенно:

Объект = ДокументСсылка.ПолучитьОбъект();
Комментарий = Объект.Комментарий;
Сумма = Объект.СуммаДокумента;

Со ссылкой всё тоньше. Каждое обращение к нестандартному реквизиту ссылки — это отдельный запрос к базе данных. Платформа читает значение лениво, в момент разыменования:

Наим = Номенклатура.Наименование;   // запрос в базу
Арт  = Номенклатура.Артикул;        // ещё один запрос

Стандартные реквизиты ссылки — Код, Наименование у справочника, Номер, Дата, Проведён у документа — часто уже есть в кэше ссылки, и обращение к ним дешевле. А вот Артикул, Производитель и любые добавленные реквизиты каждый раз вызывают чтение из базы.

Отдельно про ПолучитьОбъект(). Метод загружает запись целиком — вместе со всеми табличными частями — и ставит объектную блокировку. Для чтения одного поля это перебор. Получайте объект только когда собираетесь его менять и записывать; для чтения достаточно ссылки или запроса.

В цикле по списку ссылок разыменование точкой убивает скорость. Тысяча строк и два разыменования на строку — две тысячи запросов. Для списков собирайте данные одним запросом (см. раздел про запрос).

Цепочку точек платформа разворачивает слева направо:

ГородКонтрагента = Документ.Контрагент.ЮридическийАдрес.Город;

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

Доступ к реквизиту по имени: квадратные скобки

Когда имя реквизита приходит в переменной или строкой, точка не поможет — компилятор ждёт конкретный идентификатор. Тут работают квадратные скобки:

ИмяРеквизита = "Артикул";
Значение = Номенклатура[ИмяРеквизита];

Скобки понимают и объект, и ссылку, и строку табличной части, и структуру:

Для Каждого Строка Из Объект.Товары Цикл
    Кол = Строка["Количество"];
КонецЦикла;

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

Скобки незаменимы в универсальных обработках: когда одна процедура печатает или выгружает произвольный набор полей, список которых задаёт пользователь или настройка.

Функции общего модуля: ЗначениеРеквизитаОбъекта и ЗначенияРеквизитовОбъекта

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

Одно значение:

Наим = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Наименование");

Сразу несколько — через запятую, результат приходит структурой:

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

Разница с точкой принципиальная: три обращения через точку к ссылке — это три запроса, а ЗначенияРеквизитовОбъекта с тремя полями — один. На единичной записи незаметно, на списке — кратная разница.

Имена полей можно разыменовывать и внутри строки запроса функции, через точку:

Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
    Ссылка, "Контрагент.ИНН,Контрагент.Наименование");

Когда ссылок много, в БСП есть версия для массива — ЗначенияРеквизитовОбъектов (во множественном числе). Она принимает массив ссылок и возвращает соответствие «ссылка → структура значений», читая всё одним запросом:

Значения = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(
    МассивСсылок, "Наименование,Артикул");
Для Каждого КлючЗначение Из Значения Цикл
    Арт = КлючЗначение.Значение.Артикул;
КонецЦикла;

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

Если имени функции ОбщегоНазначения в контексте нет — значит, БСП в конфигурации нет или модуль называется иначе. Тогда остаются точка и запрос.

Разыменование реквизитов в запросе

Когда объектов много, читать их поля надо запросом. В языке запросов точка тоже разыменовывает ссылку, но платформа превращает её в соединение (JOIN) и тянет всё одним обращением к базе:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Товары.Номенклатура КАК Номенклатура,
|    Товары.Номенклатура.Артикул КАК Артикул,
|    Товары.Номенклатура.Производитель.Наименование КАК Производитель
|ИЗ
|    Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
|    Товары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Артикул + " / " + Выборка.Производитель);
КонецЦикла;

Здесь Номенклатура.Артикул и Номенклатура.Производитель.Наименование — то же разыменование, но ценой одного запроса на всю выборку, а не на каждую строку.

Разыменование в запросе даёт левое соединение. Если ссылка пустая или «битая», в поле придёт NULL, а не пустое значение. Оборачивайте такие поля в ЕСТЬNULL(Поле, ""), иначе сравнение и сложение дадут неожиданный результат.

Выбирать Товары.Номенклатура.Производитель в запросе почти всегда правильнее, чем читать то же самое точкой в цикле по результату — одно соединение против сотен обращений.

Реквизиты в табличных частях и универсальных коллекциях

У строки табличной части реквизиты читаются как у любого объекта — через точку или по имени:

Строка = Объект.Товары[0];
Ном = Строка.Номенклатура;
Кол = Строка["Количество"];

Перебор строк — обычным циклом:

Для Каждого Строка Из Объект.Товары Цикл
    Итог = Итог + Строка.Сумма;
КонецЦикла;

Со структурами и соответствиями «реквизит» — это элемент коллекции. У структуры к значению можно обратиться точкой, скобками или безопасно через метод Свойство:

Если Параметры.Свойство("Склад", ЗначениеСклада) Тогда
    // ключ есть, ЗначениеСклада заполнено
КонецЕсли;

У соответствия значение достают методом Получить — он вернёт Неопределено, если ключа нет:

Значение = Настройки.Получить("Валюта");
Если Значение = Неопределено Тогда
    Значение = ВалютаПоУмолчанию;
КонецЕсли;

Разница важная: у структуры прямое обращение Параметры.Склад к несуществующему ключу вызовет ошибку, а Свойство(...) и Получить(...) — нет. Для данных, в полноте которых вы не уверены, берите безопасные методы.

Динамические типы: метаданные и приведение значения

Если заранее не известно, какие реквизиты у объекта, их список даёт метаданные. Комбинация «коллекция реквизитов из метаданных + обращение по имени» читает всё, что есть у объекта:

Для Каждого РеквизитМД Из Номенклатура.Метаданные().Реквизиты Цикл
    Имя = РеквизитМД.Имя;
    Значение = Номенклатура[Имя];
    Сообщить(Имя + ": " + Строка(Значение));
КонецЦикла;

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

Метаданные().Реквизиты возвращает только добавленные реквизиты. Стандартные (Код, Наименование, Номер, Дата) в эту коллекцию не входят — их читайте отдельно по известным именам.

Так же достаются реквизиты табличной части через её метаданные: Номенклатура.Метаданные().ТабличныеЧасти, у каждой — своя коллекция Реквизиты. Этот приём лежит в основе универсальных выгрузок, сравнений объектов и правил обмена.

Отдельная сложность — реквизиты составного типа. Если реквизит имеет составной тип (например, Плательщик может быть и юрлицом, и физлицом), перед обращением к его вложенному реквизиту тип надо уточнить. В коде — через ТипЗнч:

Если ТипЗнч(Объект.Плательщик) = Тип("СправочникСсылка.Контрагенты") Тогда
    ИНН = Объект.Плательщик.ИНН;
КонецЕсли;

В запросе для этого есть оператор ВЫРАЗИТЬ:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Таблица.Плательщик КАК Справочник.Контрагенты).ИНН КАК ИНН
ИЗ ...

Без приведения запрос вернёт NULL для строк, где тип не совпал. Приведение — обязательный шаг для составных реквизитов, иначе половина данных теряется молча, без ошибки.

Типичные ошибки и диагностика

Чаще всего обращение к реквизиту падает на имени или на типе переменной. Разберите симптом — и причина обычно очевидна:

Диагностика: выберите симптом

Ещё две частые ловушки. Обращение к реквизиту пустой ссылки (Справочники.Номенклатура.ПустаяСсылка()) ошибки не даёт — вернётся пустое значение по типу реквизита. А вот точка у Неопределено падает всегда. Поэтому результат методов, которые могут вернуть Неопределено, проверяйте до разыменования.

Какой способ выбрать

Коротко: что на руках — тем и читайте.

Что естьНужноСпособ
Объект в памятилюбое полеточка Объект.Реквизит
Ссылка, 1–2 полязначениеЗначениеРеквизитаОбъекта
Ссылка, много полейзначенияЗначенияРеквизитовОбъекта
Имя реквизита в переменнойзначениескобки Объект[Имя]
Много ссылок, списокнабор полейзапрос с разыменованием
Набор реквизитов неизвестенвсё подрядметаданные + скобки
Что у вас на руках и что нужно прочитать?

Частые вопросы

Чем обращение к реквизиту ссылки отличается от обращения к объекту?
Объект уже загружен в память, его реквизиты читаются мгновенно. Ссылка читает нестандартный реквизит из базы в момент разыменования — это отдельный запрос при каждом обращении.
Можно ли обратиться к реквизиту, если имя известно только во время работы?
Да, через квадратные скобки: Объект[ИмяРеквизита], где ИмяРеквизита — строковая переменная. Работает для объекта, ссылки и строки табличной части.
Почему чтение реквизитов в цикле по списку так тормозит?
Каждое разыменование ссылки — отдельное обращение к базе. На списке из тысяч элементов это тысячи запросов. Выносите чтение в один запрос или в ЗначенияРеквизитовОбъекта.
Как прочитать реквизит составного типа?
Сначала уточните тип: в коде через ТипЗнч, в запросе через ВЫРАЗИТЬ(Поле КАК Справочник.Имя). Без приведения вложенный реквизит вернёт NULL.
Что вернёт обращение к реквизиту пустой ссылки?
Пустое значение по типу реквизита, а не ошибку. Ошибку даёт только обращение через точку к Неопределено — его проверяйте заранее.