К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как в 1С определить: объект или ссылка

Как в 1С определить, является ли значение объектом или ссылкой. Методы СсылкаНаОбъект, ПолучитьОбъект, проверка типов, типичные ошибки и примеры кода.

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

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

Ниже разобраны практические способы проверки типа значения, различия в поведении ссылки и объекта, типичные ошибки и критерии выбора подхода в разных сценариях.

Чем ссылка отличается от объекта в 1С

Ссылка — это уникальный идентификатор записи, содержащий по сути только GUID и указание на таблицу. Платформа умеет «разыменовывать» ссылку: при обращении к реквизиту через точку (Товар.Наименование) она незаметно выполняет запрос к базе и возвращает значение. Ссылка занимает минимум памяти, быстро передаётся между сервером и клиентом и безопасно сериализуется.

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

Важный нюанс: у ссылки тоже доступны реквизиты на чтение, но попытка присвоить значение реквизиту ссылки вызовет ошибку исполнения. И наоборот — сравнение объекта со ссылкой оператором «=» не сработает так, как ожидает разработчик, потому что сравниваются разные типы.

Как определить, что значение является ссылкой

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

Если СсылкаНаОбъект(МоеЗначение) Тогда

// Это ссылка — можно читать, сравнивать, передавать

Иначе

// Это не ссылка: объект, число, строка и т.д.

КонецЕсли;

Альтернативный вариант — проверка конкретного типа через ТипЗнч() и Тип(). Это нужно, когда важно не просто «ссылка ли это», а ссылка какого именно вида:

Если ТипЗнч(МоеЗначение) = Тип("СправочникСсылка.Номенклатура") Тогда

// Это ссылка именно на справочник Номенклатура

КонецЕсли;

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

bsl
Функция ЭтоСсылка(Значение) Экспорт
	Возврат СсылкаНаОбъект(Значение);
КонецФункции

Как определить, что значение является объектом

Для объектов прямой универсальной функции-аналога СсылкаНаОбъект() нет, поэтому проверку строят на имени типа. Имя типа объекта всегда заканчивается на слово «Объект»: СправочникОбъект.Номенклатура, ДокументОбъект.РеализацияТоваровУслуг, тогда как у ссылки — «Ссылка».

ИмяТипа = Строка(ТипЗнч(МоеЗначение));

Если СтрНайти(ИмяТипа, "Объект") > 0 Тогда

// Это объект — можно менять реквизиты и вызывать Записать()

ИначеЕсли СтрНайти(ИмяТипа, "Ссылка") > 0 Тогда

// Это ссылка

КонецЕсли;

Более строгий вариант без разбора строк — проверка наличия характерных методов через попытку обращения или сравнение с конкретным типом: ТипЗнч(Значение) = Тип("ДокументОбъект.РеализацияТоваровУслуг"). Также признаком объекта служит доступность метода ЭтоНовый() и свойства Модифицированность() — у ссылок их нет.

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

Как получить объект из ссылки и ссылку из объекта

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

ОбъектТовара = СсылкаНаТовар.ПолучитьОбъект();

ОбъектТовара.Наименование = "Новое наименование";

ОбъектТовара.Записать();

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

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

Порядок безопасного изменения данных
  1. 1
    Проверить, что значение — ссылка: СсылкаНаОбъект(Значение) = Истина
  2. 2
    Убедиться, что ссылка существует в базе (запрос или функция БСП)
  3. 3
    Получить объект: Объект = Ссылка.ПолучитьОбъект()
  4. 4
    Изменить реквизиты и вызвать Объект.Записать()
  5. 5
    Проверить результат: перечитать данные по ссылке или обработать исключение

Когда использовать ссылку, а когда объект

Выбор определяется задачей. Если нужно только прочитать реквизиты, сравнить значения, заполнить реквизит документа или передать параметр в запрос — работайте со ссылкой. Запросы вообще оперируют только ссылками: результат выборки содержит ссылки, и это правильно с точки зрения производительности.

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

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

Сравнение вариантов

Ссылка

Чтение реквизитов, сравнение, параметры запросов, передача между клиентом и сервером. Не позволяет изменять данные.

Объект

Изменение и запись данных, проведение документов, установка пометки удаления. Требует обращения к базе, не передаётся между клиентом и сервером.

Запрос

Массовое чтение и анализ данных без загрузки объектов. Не подходит для изменения записей.

Типичные ошибки при работе со ссылками и объектами

Самая распространённая ошибка — попытка присвоить значение реквизиту ссылки: Ссылка.Наименование = "...". Платформа выдаст ошибку «Поле объекта недоступно для записи». Лечится получением объекта перед изменением.

Вторая частая проблема — сравнение объекта со ссылкой. Выражение Объект = Ссылка вернёт Ложь даже для одной и той же записи, потому что типы разные. Корректно сравнивать Объект.Ссылка = Ссылка.

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

Типичные ошибки
01
Присвоение реквизиту ссылки — ошибка «Поле объекта недоступно для записи»; сначала получите объект
02
Сравнение объекта со ссылкой оператором «=» — всегда Ложь; сравнивайте Объект.Ссылка со ссылкой
03
Вызов ПолучитьОбъект() в цикле ради чтения — резкое падение производительности; используйте запрос
04
Передача объекта в серверную процедуру управляемой формы — ошибка сериализации; передавайте ссылку
05
Изменение объекта без Записать() — данные в базе не сохраняются

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

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

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

Контрольные точки

Как проверить результат

Тип значения в отладчике
Норма

СправочникСсылка.Х / ДокументСсылка.Х для чтения, ...Объект.Х для изменения

Тревожный признак

Тип не соответствует ожиданию, значение Неопределено

СсылкаНаОбъект(Значение)
Норма

Истина для ссылки, Ложь для объекта и примитивов

Тревожный признак

Результат противоречит логике кода — проверьте источник значения

Запись объекта
Норма

После Записать() реквизиты читаются через ссылку с новыми значениями

Тревожный признак

Данные в базе не изменились — Записать() не вызван или отменена транзакция

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

Чем проверка СсылкаНаОбъект() лучше сравнения типов?

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

Можно ли изменить данные через ссылку без ПолучитьОбъект()?

Нет. Ссылка предназначена только для чтения и идентификации. Любое изменение записи в базе выполняется через объект с последующим вызовом Записать() либо через программные механизмы вроде УстановитьЗначение константы — но не через реквизиты ссылки.

Почему Объект = Ссылка возвращает Ложь, хотя это одна запись?

Потому что сравниваются значения разных типов: ДокументОбъект и ДокументСсылка. Платформа не выполняет неявного приведения. Правильное сравнение — Объект.Ссылка = Ссылка.

Как проверить «битую» ссылку?

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

Можно ли передать объект с клиента на сервер?

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

Что возвращает ПолучитьОбъект() для несуществующей записи?

Для битой ссылки метод обычно возвращает Неопределено. Поэтому перед работой с результатом стоит проверить его на Неопределено, особенно в коде обменов и обработок, где ссылки могут указывать на удалённые записи.

Итог

Что важно запомнить

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