Чтение реквизитов объекта во встроенном языке 1С
Содержание
Чтобы обратиться к реквизиту объекта 1С, в большинстве случаев хватает точки: Номенклатура.Артикул, Документ.Дата, СтрокаТабличнойЧасти.Цена. Приём одинаково работает и для ссылки, и для объекта, и для строки табличной части — слева имя переменной, справа имя реквизита как в конфигураторе.
Когда полей много, имя реквизита известно только во время работы или читать нужно сразу по списку объектов, точка перестаёт быть лучшим выбором. Ниже — все рабочие способы получить значение реквизита: обращение по имени через квадратные скобки, функции общего модуля из БСП, разыменование в запросе и перебор через метаданные. С примерами кода и ценой каждого способа по скорости.
Обращение через точку: разыменование и его цена
Точка — это разыменование (dereferencing). У объекта, который уже загружен в память, реквизит читается мгновенно:
Объект = ДокументСсылка.ПолучитьОбъект();
Комментарий = Объект.Комментарий;
Сумма = Объект.СуммаДокумента;
Со ссылкой всё тоньше. Каждое обращение к нестандартному реквизиту ссылки — это отдельный запрос к базе данных. Платформа читает значение лениво, в момент разыменования:
Наим = Номенклатура.Наименование; // запрос в базу
Арт = Номенклатура.Артикул; // ещё один запрос
Стандартные реквизиты ссылки — Код, Наименование у справочника, Номер, Дата, Проведён у документа — часто уже есть в кэше ссылки, и обращение к ним дешевле. А вот Артикул, Производитель и любые добавленные реквизиты каждый раз вызывают чтение из базы.
Отдельно про ПолучитьОбъект(). Метод загружает запись целиком — вместе со всеми табличными частями — и ставит объектную блокировку. Для чтения одного поля это перебор. Получайте объект только когда собираетесь его менять и записывать; для чтения достаточно ссылки или запроса.
В цикле по списку ссылок разыменование точкой убивает скорость. Тысяча строк и два разыменования на строку — две тысячи запросов. Для списков собирайте данные одним запросом (см. раздел про запрос).
Цепочку точек платформа разворачивает слева направо:
ГородКонтрагента = Документ.Контрагент.ЮридическийАдрес.Город;
Это три последовательных разыменования, три запроса. Удобно, но дорого — закладывайте это в код, который работает с одной записью, а не с тысячами.
Доступ к реквизиту по имени: квадратные скобки
Когда имя реквизита приходит в переменной или строкой, точка не поможет — компилятор ждёт конкретный идентификатор. Тут работают квадратные скобки:
ИмяРеквизита = "Артикул";
Значение = Номенклатура[ИмяРеквизита];
Скобки понимают и объект, и ссылку, и строку табличной части, и структуру:
Для Каждого Строка Из Объект.Товары Цикл
Кол = Строка["Количество"];
КонецЦикла;
Это тот же механизм разыменования, что и точка, просто имя задаётся строкой во время работы. Отсюда и главный риск: ошибку в имени компилятор не поймает, она всплывёт только при запуске.
Скобки незаменимы в универсальных обработках: когда одна процедура печатает или выгружает произвольный набор полей, список которых задаёт пользователь или настройка.
Функции общего модуля: ЗначениеРеквизитаОбъекта и ЗначенияРеквизитовОбъекта
Если в конфигурации подключена Библиотека стандартных подсистем (БСП), читать реквизиты ссылки лучше через методы общего модуля ОбщегоНазначения. Они делают ровно один запрос и не грузят объект целиком.
Одно значение:
Наим = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Наименование");
Сразу несколько — через запятую, результат приходит структурой:
Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
Ссылка, "Наименование,Артикул,Производитель");
Наим = Данные.Наименование;
Арт = Данные.Артикул;
Произв = Данные.Производитель;
Разница с точкой принципиальная: три обращения через точку к ссылке — это три запроса, а ЗначенияРеквизитовОбъекта с тремя полями — один. На единичной записи незаметно, на списке — кратная разница.
Имена полей можно разыменовывать и внутри строки запроса функции, через точку:
Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
Ссылка, "Контрагент.ИНН,Контрагент.Наименование");
Когда ссылок много, в БСП есть версия для массива — ЗначенияРеквизитовОбъектов (во множественном числе). Она принимает массив ссылок и возвращает соответствие «ссылка → структура значений», читая всё одним запросом:
Значения = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(
МассивСсылок, "Наименование,Артикул");
Для Каждого КлючЗначение Из Значения Цикл
Арт = КлючЗначение.Значение.Артикул;
КонецЦикла;
Это и есть правильный способ прочитать поля по списку: ни одного обращения в цикле, один запрос на весь массив.
Если имени функции ОбщегоНазначения в контексте нет — значит, БСП в конфигурации нет или модуль называется иначе. Тогда остаются точка и запрос.
Разыменование реквизитов в запросе
Когда объектов много, читать их поля надо запросом. В языке запросов точка тоже разыменовывает ссылку, но платформа превращает её в соединение (JOIN) и тянет всё одним обращением к базе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| Товары.Номенклатура.Артикул КАК Артикул,
| Товары.Номенклатура.Производитель.Наименование КАК Производитель
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Артикул + " / " + Выборка.Производитель);
КонецЦикла;
Здесь Номенклатура.Артикул и Номенклатура.Производитель.Наименование — то же разыменование, но ценой одного запроса на всю выборку, а не на каждую строку.
Разыменование в запросе даёт левое соединение. Если ссылка пустая или «битая», в поле придёт NULL, а не пустое значение. Оборачивайте такие поля в ЕСТЬNULL(Поле, ""), иначе сравнение и сложение дадут неожиданный результат.
Выбирать Товары.Номенклатура.Производитель в запросе почти всегда правильнее, чем читать то же самое точкой в цикле по результату — одно соединение против сотен обращений.
Реквизиты в табличных частях и универсальных коллекциях
У строки табличной части реквизиты читаются как у любого объекта — через точку или по имени:
Строка = Объект.Товары[0];
Ном = Строка.Номенклатура;
Кол = Строка["Количество"];
Перебор строк — обычным циклом:
Для Каждого Строка Из Объект.Товары Цикл
Итог = Итог + Строка.Сумма;
КонецЦикла;
Со структурами и соответствиями «реквизит» — это элемент коллекции. У структуры к значению можно обратиться точкой, скобками или безопасно через метод Свойство:
Если Параметры.Свойство("Склад", ЗначениеСклада) Тогда
// ключ есть, ЗначениеСклада заполнено
КонецЕсли;
У соответствия значение достают методом Получить — он вернёт Неопределено, если ключа нет:
Значение = Настройки.Получить("Валюта");
Если Значение = Неопределено Тогда
Значение = ВалютаПоУмолчанию;
КонецЕсли;
Разница важная: у структуры прямое обращение Параметры.Склад к несуществующему ключу вызовет ошибку, а Свойство(...) и Получить(...) — нет. Для данных, в полноте которых вы не уверены, берите безопасные методы.
Динамические типы: метаданные и приведение значения
Если заранее не известно, какие реквизиты у объекта, их список даёт метаданные. Комбинация «коллекция реквизитов из метаданных + обращение по имени» читает всё, что есть у объекта:
Для Каждого РеквизитМД Из Номенклатура.Метаданные().Реквизиты Цикл
Имя = РеквизитМД.Имя;
Значение = Номенклатура[Имя];
Сообщить(Имя + ": " + Строка(Значение));
КонецЦикла;
Обратите внимание на Строка(Значение) при выводе — реквизит может быть числом, датой, ссылкой, и склеивать его со строкой напрямую нельзя.
Метаданные().Реквизиты возвращает только добавленные реквизиты. Стандартные (Код, Наименование, Номер, Дата) в эту коллекцию не входят — их читайте отдельно по известным именам.
Так же достаются реквизиты табличной части через её метаданные: Номенклатура.Метаданные().ТабличныеЧасти, у каждой — своя коллекция Реквизиты. Этот приём лежит в основе универсальных выгрузок, сравнений объектов и правил обмена.
Отдельная сложность — реквизиты составного типа. Если реквизит имеет составной тип (например, Плательщик может быть и юрлицом, и физлицом), перед обращением к его вложенному реквизиту тип надо уточнить. В коде — через ТипЗнч:
Если ТипЗнч(Объект.Плательщик) = Тип("СправочникСсылка.Контрагенты") Тогда
ИНН = Объект.Плательщик.ИНН;
КонецЕсли;
В запросе для этого есть оператор ВЫРАЗИТЬ:
ВЫБРАТЬ
ВЫРАЗИТЬ(Таблица.Плательщик КАК Справочник.Контрагенты).ИНН КАК ИНН
ИЗ ...
Без приведения запрос вернёт NULL для строк, где тип не совпал. Приведение — обязательный шаг для составных реквизитов, иначе половина данных теряется молча, без ошибки.
Типичные ошибки и диагностика
Чаще всего обращение к реквизиту падает на имени или на типе переменной. Разберите симптом — и причина обычно очевидна:
Ещё две частые ловушки. Обращение к реквизиту пустой ссылки (Справочники.Номенклатура.ПустаяСсылка()) ошибки не даёт — вернётся пустое значение по типу реквизита. А вот точка у Неопределено падает всегда. Поэтому результат методов, которые могут вернуть Неопределено, проверяйте до разыменования.
Какой способ выбрать
Коротко: что на руках — тем и читайте.
| Что есть | Нужно | Способ |
|---|---|---|
| Объект в памяти | любое поле | точка Объект.Реквизит |
| Ссылка, 1–2 поля | значение | ЗначениеРеквизитаОбъекта |
| Ссылка, много полей | значения | ЗначенияРеквизитовОбъекта |
| Имя реквизита в переменной | значение | скобки Объект[Имя] |
| Много ссылок, список | набор полей | запрос с разыменованием |
| Набор реквизитов неизвестен | всё подряд | метаданные + скобки |
ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Имя") — один запрос, объект не грузится. Для нескольких полей — ЗначенияРеквизитовОбъекта через запятую.Объект = Ссылка.ПолучитьОбъект(), дальше читайте и пишите реквизиты через точку.Объект[ИмяРеквизита].Частые вопросы
Чем обращение к реквизиту ссылки отличается от обращения к объекту?
Можно ли обратиться к реквизиту, если имя известно только во время работы?
Объект[ИмяРеквизита], где ИмяРеквизита — строковая переменная. Работает для объекта, ссылки и строки табличной части.Почему чтение реквизитов в цикле по списку так тормозит?
ЗначенияРеквизитовОбъекта.Как прочитать реквизит составного типа?
ТипЗнч, в запросе через ВЫРАЗИТЬ(Поле КАК Справочник.Имя). Без приведения вложенный реквизит вернёт NULL.Что вернёт обращение к реквизиту пустой ссылки?
Неопределено — его проверяйте заранее.