Как устроено значение объекта в 1С 8.3 и что с ним делать в коде

· 9 мин

Содержание

Значение объекта в 1С — это не «то, что лежит в поле», а один из двух разных видов данных: ссылка (указатель на запись в базе) или объект (полная копия записи в памяти, которую можно изменить и записать). Переменная Номенклатура в коде почти всегда хранит ссылку типа СправочникСсылка.Номенклатура. Чтобы поменять реквизит, нужен объект: Ссылка.ПолучитьОбъект().

Частая ошибка начинающих — присвоить реквизиту ссылки новое значение и ждать, что оно сохранится. Ссылка только читает. Всё ниже — про платформу 1С:Предприятие 8.3 и встроенный язык, от конкретной конфигурации не зависит.

Ссылка и объект: что хранится в памяти

У каждого объекта метаданных ссылочного типа (справочник, документ, план видов характеристик, план счетов , бизнес-процесс, задача) платформа создаёт несколько типов. Для справочника Номенклатура это:

ТипЧто хранитМожно менять?
СправочникСсылка.Номенклатуратип и уникальный идентификатор записинет, только читать
СправочникОбъект.Номенклатуравсе реквизиты и табличные части, прочитанные в памятьда, затем Записать()
СправочникВыборка.Номенклатуракурсор по нескольким записямнет
СправочникМенеджер.Номенклатураточку входа: поиск, создание, выборка—

Ссылка весит мало. Её можно передавать между клиентом и сервером, класть в массив, хранить в реквизите другого документа. В базе ссылка — это пара «тип + уникальный идентификатор».

Объект тяжелее. При ПолучитьОбъект() платформа читает из базы запись целиком, вместе с табличными частями. У документа на 5 000 строк товаров это ощутимо. Объект существует только на сервере (и в толстом клиенте), на тонкий клиент его не передать.

💡 Когда пишете Ссылка.Наименование, платформа читает данные объекта, а не одно поле. В цикле по тысяче ссылок это тысяча чтений. Для одного-двух реквизитов в типовых конфигурациях на БСП есть функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта() и ОбщегоНазначения.ЗначенияРеквизитовОбъекта() — они делают запрос только к нужным полям.

Значение и ссылка в обычных переменных

Коллекции в 1С (Массив, Структура, Соответствие, ТаблицаЗначений) при присваивании не копируются:

Список1 = Новый Массив;
Список1.Добавить(10);
Список2 = Список1;
Список2.Добавить(20);
// Список1.Количество() = 2: обе переменные указывают на один массив

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

Какие типы значений бывают и чем они отличаются

Все значения встроенного языка удобно разложить на пять групп.

  • 🔹 Примитивные: Число, Строка, Дата, Булево, Неопределено, Null, Тип. Копируются при присваивании, сравниваются по содержимому.
  • 🔗 Ссылочные: ДокументСсылка.*, СправочникСсылка.*, ПеречислениеСсылка.* и т. п. Сравниваются по идентификатору: две ссылки на одну запись равны.
  • 📦 Объектные: ДокументОбъект.*, СправочникОбъект.*. Это изменяемые копии записей.
  • 🗂 Наборы записей регистров: РегистрСведенийНаборЗаписей.*, РегистрНакопленияНаборЗаписей.*. У регистров ссылок нет, запись идентифицируется отбором по измерениям и регистратору.
  • 🧰 Универсальные коллекции: Массив, Структура, Соответствие, СписокЗначений, ТаблицаЗначений, ДеревоЗначений и их фиксированные варианты.

Неопределено и Null — не одно и то же

Неопределено — значение по умолчанию у новой переменной и у пустого реквизита составного типа. Null приходит из запроса, когда левое соединение не нашло пару. Код Если Значение = Неопределено не поймает Null. В тексте запроса обрабатывайте это через ЕСТЬNULL(), в коде — сравнением с Null.

Пустая ссылка

У реквизита ссылочного типа, который не заполнили, значение не Неопределено, а пустая ссылка этого типа. Проверка:

Если Ссылка.Пустая() Тогда
    // реквизит не заполнен
КонецЕсли;

// универсально для любого типа
Если Не ЗначениеЗаполнено(Значение) Тогда
    // пусто: 0, "", пустая дата, пустая ссылка, Неопределено
КонецЕсли;

ЗначениеЗаполнено() удобнее, когда тип заранее неизвестен.

Методы объекта: чтение, изменение, запись

Типовой цикл работы с объектом короткий: получить, изменить, записать.

Ссылка = Справочники.Номенклатура.НайтиПоКоду("00-000123");
Если Ссылка.Пустая() Тогда
    Сообщить("Элемент не найден");
    Возврат;
КонецЕсли;

НоменклатураОбъект = Ссылка.ПолучитьОбъект();
НоменклатураОбъект.Наименование = "Кабель ВВГ 3х2,5";
НоменклатураОбъект.Записать();

НайтиПоКоду() и НайтиПоНаименованию() при неудаче возвращают пустую ссылку, а не Неопределено. Поэтому проверка именно через Пустая().

Методы, которые нужны чаще всего

МетодЧто делаетГде вызывать
ПолучитьОбъект()читает объект по ссылкесервер
Записать()сохраняет объект; у документа принимает режим записисервер
Записать(РежимЗаписиДокумента.Проведение)записывает и проводит документсервер
Модифицированность()показывает, меняли ли объект после чтениясервер
Скопировать()создаёт новый несохранённый объект с теми же даннымисервер
Заблокировать()ставит объектную блокировку перед правкойсервер
Прочитать()перечитывает объект из базы, отменяя изменения в памятисервер
Метаданные()возвращает объект метаданных для анализа структурысервер
УстановитьПометкуУдаления(Истина)помечает на удалениесервер

У нового объекта, созданного через Справочники.Номенклатура.СоздатьЭлемент(), ссылка пустая до первой записи. Если ссылка нужна раньше, например для связанных записей в одной транзакции, назначьте её заранее методом УстановитьСсылкуНового().

ДополнительныеСвойства и ОбменДанными.Загрузка

Два приёма, которые знает каждый, кто писал обработки массовых изменений:

  1. Объект.ДополнительныеСвойства — структура, которая живёт только в памяти. Через неё передают флаги в подписки на события и в модуль объекта, не создавая лишних реквизитов.
  2. Объект.ОбменДанными.Загрузка = Истина — запись без выполнения проверок и большей части кода в обработчиках ПередЗаписью и ПриЗаписи (там это условие проверяется в типовом коде). Так пишут при загрузке данных из обмена.

Режим ОбменДанными.Загрузка отключает проверки и часть логики конфигурации. Документ не перепроведётся, связанные регистры могут не обновиться. Используйте его только когда точно знаете, какую логику пропускаете.

Объект в управляемой форме: реквизит «Объект»

В форме элемента или документа основной реквизит называется Объект, но это не СправочникОбъект. Это ДанныеФормыСтруктура — копия данных, приспособленная для передачи на клиент. Методов Записать() и ПолучитьОбъект() у неё нет.

Чтобы вызвать метод модуля объекта, на сервере нужно преобразование:

&НаСервере
Процедура ЗаполнитьНаСервере()
    ДокументОбъект = РеквизитФормыВЗначение("Объект");
    ДокументОбъект.ЗаполнитьТовары(); // экспортный метод модуля объекта
    ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры

Пара РеквизитФормыВЗначение / ЗначениеВРеквизитФормы работает с реквизитом по имени. Для произвольных данных формы есть ДанныеФормыВЗначение() и ЗначениеВДанныеФормы(). Так же реквизит формы типа ТаблицаЗначений превращается в настоящую таблицу значений, у которой работают Свернуть(), Сортировать() и Скопировать().

💡 После ЗначениеВРеквизитФормы форма показывает новые данные, но в базу ничего не записано. Записывает пользователь кнопкой формы или ваш код через Записать() у объекта.

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

Тип значения узнают функцией ТипЗнч() и сравнивают с Тип():

Если ТипЗнч(Значение) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
    // работаем с реализацией
ИначеЕсли ТипЗнч(Значение) = Тип("Число") Тогда
    // число
КонецЕсли;

Для реквизитов составного типа удобна проверка по описанию: Новый ОписаниеТипов("Число, Строка").СодержитТип(ТипЗнч(Значение)).

Строка, число, дата

ЗадачаКак сделатьПодвох
Число в строку без пробеловФормат(Сумма, "ЧГ=0")Строка(1234567) даст разделители групп
Число в строку для обменаXMLСтрока(Сумма)разделитель — точка, без пробелов
Строка в числоXMLЗначение(Тип("Число"), "12.5")строка должна быть в формате XML
Безопасное приведениеНовый ОписаниеТипов("Число").ПривестиЗначение(Значение)непреобразуемое станет 0
Строка в датуДата("20261007")ожидается формат ГГГГММДД или ГГГГММДДччммсс
Строка в булевоXMLЗначение(Тип("Булево"), "true")только true / false

Ссылка из строки и обратно

Строка(Ссылка) возвращает представление («Кабель ВВГ 3х2,5»), а не идентификатор. Для хранения и передачи нужен идентификатор:

// ссылка -> строка
СтрокаУИД = Строка(Ссылка.УникальныйИдентификатор());

// строка -> ссылка
УИД = Новый УникальныйИдентификатор(СтрокаУИД);
Ссылка = Справочники.Номенклатура.ПолучитьСсылку(УИД);

ПолучитьСсылку() вернёт ссылку, даже если такой записи в базе нет. Проверьте её: если Ссылка.ПолучитьОбъект() вернул Неопределено, ссылка битая. В отчётах и формах такие ссылки видны как <Объект не найден> с идентификатором в скобках.

Сериализация: сохранить значение и восстановить его

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

СпособЧто умеетДля чего
ЗначениеВСтрокуВнутр() / ЗначениеИзСтрокиВнутр()почти любое значение, включая коллекцииобмен внутри одной базы, отладка
СериализаторXDTO.ЗаписатьXML() / ПрочитатьXML()объекты, ссылки, наборы записей, коллекциивыгрузка и перенос объектов между базами
ЗаписатьJSON()примитивы, Структура, Соответствие, МассивREST и HTTP-сервисы
ХранилищеЗначениялюбое сериализуемое значение, со сжатиемреквизит типа «Хранилище значения»

Выгрузка объекта в XML-строку и обратно:

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ДокументОбъект);
ТекстXML = ЗаписьXML.Закрыть();

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ТекстXML);
Восстановленный = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
ЧтениеXML.Закрыть();

Хранилище значения со сжатием:

Хранилище = Новый ХранилищеЗначения(ТаблицаНастроек, Новый СжатиеДанных(9));
// ... запись Хранилище в реквизит объекта
ТаблицаНастроек = Хранилище.Получить();

Формат ЗначениеВСтрокуВнутр внутренний. Не стройте на нём обмен с внешними системами и не храните результат годами: после изменения метаданных строка может не восстановиться.

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

Ошибки при работе со значениями объектов

Большинство ошибок здесь — следствие путаницы между ссылкой, объектом, Неопределено и Null.

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

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

Ещё одна ловушка — сравнение объекта со ссылкой. ДокументОбъект = Ссылка всегда даёт Ложь, даже если это одна запись. Сравнивайте ДокументОбъект.Ссылка = Ссылка.

Если в обработке массовой правки объекты пишутся в цикле, сначала прогоните её на копии базы. Ошибка в цикле по 10 000 документов перезапишет данные во всех, а откат возможен только из резервной копии.

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

Чем значение объекта отличается от значения реквизита?
Значение объекта — это вся запись: ссылка или объект целиком. Значение реквизита — одно поле этой записи, которое может быть примитивом или ссылкой на другую запись.
Почему у формы есть Объект, но нет метода Записать?
Реквизит формы «Объект» имеет тип данных формы, а не СправочникОбъект. Преобразуйте его на сервере через РеквизитФормыВЗначение или запишите форму штатной командой.
Можно ли передать СправочникОбъект с сервера на тонкий клиент?
Нет. Передавайте ссылку, структуру с нужными полями или данные формы. Объект создаётся и меняется только на сервере.
Как быстро прочитать два реквизита по ссылке без загрузки всего объекта?
Запросом к таблице объекта с отбором по ссылке или функцией БСП ОбщегоНазначения.ЗначенияРеквизитовОбъекта, которая возвращает структуру.
Почему две одинаковые структуры не равны при сравнении?
Коллекции сравниваются как экземпляры, а не по содержимому. Две разные структуры с одинаковыми полями дадут Ложь, а две ссылки на одну запись — Истина.