Ссылка или Объект
Вопрос №1 на собеседованиях по 1С: «Чем ссылка на элемент отличается от объекта?» Кто путается здесь — обычно проваливает и остальное.
Ссылка — это лёгкий указатель на запись в базе (кто это), Объект — тяжёлая копия всех данных записи в памяти, которую можно менять и записывать (что внутри).
Что стоит за вопросом
В 1С у каждого справочника и документа есть два разных типа. Например, для справочника «Контрагенты»: СправочникСсылка.Контрагенты и СправочникОбъект.Контрагенты. Их путают почти все новички, а на них держится половина кода конфигурации, поэтому вопрос и стал классикой.
- Ссылка — это, по сути, идентификатор записи (внутри — уникальный GUID). Она лёгкая: не грузит все поля, живёт в реквизитах, параметрах запросов, полях документов. Через ссылку можно читать реквизиты (
Контрагент.ИНН), но нельзя менять и записывать. - Объект — это уже «распакованная» запись: все реквизиты, табличные части, возможность изменить и вызвать
Записать(). Он тяжелее: чтобы его получить, платформа читает из базы полную запись.
Метафора: ссылка — это номер телефона человека в записной книжке (компактно, легко хранить и сравнивать), а объект — сам человек, пришедший в офис (можно поговорить, что-то изменить, но «вызвать» его — целое дело).
Как получить объект из ссылки
Переход от ссылки к объекту — метод ПолучитьОбъект(). Обратно объект знает свою ссылку через свойство Ссылка.
// Есть ссылка (лёгкая) — читаем реквизит без загрузки всего объекта
СсылкаКонтрагента = Справочники.Контрагенты.НайтиПоКоду("00-0001");
Сообщить("ИНН: " + СсылкаКонтрагента.ИНН); // чтение через ссылку — можно
// Нужно ИЗМЕНИТЬ — получаем объект (тяжёлая операция: чтение из базы)
ОбъектКонтрагента = СсылкаКонтрагента.ПолучитьОбъект();
ОбъектКонтрагента.Комментарий = "Проверен менеджером";
ОбъектКонтрагента.Записать();
Попытка изменить реквизит прямо у ссылки — ошибка: ссылка предназначена только для чтения.
СсылкаКонтрагента.Комментарий = "Тест"; // ОШИБКА: у ссылки нет изменяемых реквизитов для записи
Результат:
{Обработка.Пример.МодульОбъекта(12)}: Ошибка при вызове метода контекста:
значение не является значением объектного типа (запись невозможна).
Почему сравнивать надо ссылки, а менять — объект
Это главная мысль урока. Чтобы понять, один и тот же ли это контрагент, сравнивают ссылки: ссылка — это тождество записи, сравнение лёгкое и корректное.
Если Документ.Контрагент = ВыбранныйКонтрагент Тогда
// сравнение ссылок: один и тот же элемент справочника
Сообщить("Это тот же контрагент");
КонецЕсли;
// А если сравнивать объекты — так делать НЕ надо:
Если Документ.Контрагент.ПолучитьОбъект() = ВыбранныйКонтрагент.ПолучитьОбъект() Тогда
// два РАЗНЫХ объекта в памяти, даже если это одна запись — сравнение бессмысленно
КонецЕсли;
Логика простая: два вызова ПолучитьОбъект() для одной записи создают две разные копии в памяти — сравнивать их как объекты нельзя. А вот ссылки на одну запись всегда равны. Менять же данные можно только у объекта: у ссылки нет Записать().
Как это работает: цена ПолучитьОбъект()
Вызов ПолучитьОбъект() — это реальное чтение полной записи из базы: все реквизиты и все табличные части поднимаются в память. Одна операция дешёвая, но в цикле по тысячам элементов это тысячи обращений к СУБД.
Поэтому правило: не получайте объект без надобности. Нужно только прочитать пару реквизитов — читайте через ссылку (или, ещё лучше, запросом сразу по многим элементам). Получайте объект лишь тогда, когда собираетесь изменить и записать данные.
// ПЛОХО: объект ради одного реквизита, да ещё в цикле
Для Каждого Стр Из ТаблицаКонтрагентов Цикл
Объект = Стр.Контрагент.ПолучитьОбъект(); // чтение всей записи из базы
Сообщить(Объект.ИНН); // а нужен был только ИНН
КонецЦикла;
// ХОРОШО: реквизит читается через ссылку, без загрузки объекта
Для Каждого Стр Из ТаблицаКонтрагентов Цикл
Сообщить(Стр.Контрагент.ИНН);
КонецЦикла;
Как отвечать
Идеальный ответ в трёх фразах: «Ссылка — лёгкий указатель на запись, объект — её полная изменяемая копия в памяти. Читать реквизиты и сравнивать на тождество можно и нужно по ссылке, потому что ссылки на одну запись всегда равны. Чтобы что-то изменить, я беру объект через ПолучитьОбъект() и вызываю Записать(), но помню, что это чтение всей записи из базы, поэтому в циклах его избегаю».
Хороший бонус — упомянуть, что запрос почти всегда возвращает ссылки, а не объекты, и это одна из причин, почему запросы быстрые.
Частые ошибки на собеседовании
- Считать, что ссылка и объект — «одно и то же, просто разные слова». Это разные типы с разной ценой и назначением.
- Пытаться менять данные через ссылку — у неё нет
Записать(). - Сравнивать элементы через
ПолучитьОбъект(): получаются две разные копии, сравнение всегда «не равно». - Дёргать
ПолучитьОбъект()в цикле ради одного реквизита — тысячи лишних чтений из базы. - Думать, что запрос возвращает объекты. Он возвращает ссылки и значения полей.
Итоги
- Ссылка — лёгкий указатель (тождество записи); объект — тяжёлая изменяемая копия с
Записать(). - Сравнивают ссылки, изменяют — объект.
ПолучитьОбъект()читает полную запись из базы — дорого, особенно в циклах.- Читать реквизиты можно через ссылку; получать объект — только когда реально меняем данные.