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

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

Как узнать в 1С: объект или ссылка

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

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

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

Ниже разобраны практические приёмы: как программно отличить ссылку от объекта, как проверить существование ссылки в базе, как получить объект из ссылки и обратно, а также типичные ошибки при таких проверках и способы их избежать.

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

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

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

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

Определяем тип значения через ТипЗначения

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

ТипЗнч = ТипЗнч(Переменная);

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

// это ссылка

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

// это объект

КонецЕсли;

Если нужно проверить принадлежность не к конкретному виду, а к целой группе (любая ссылка справочника, любая ссылка документа), используют обобщённые типы: СправочникСсылка, ДокументСсылка, ПеречислениеСсылка. Проверка «это вообще ссылка или нет» часто выполняется через Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч) в сочетании с аналогичными проверками для документов, планов видов характеристик и других ссылочных типов.

bsl
// Универсальная проверка: является ли значение любой ссылкой
Функция ЭтоСсылка(Значение)
    ТипЗнч = ТипЗнч(Значение);
    Возврат Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч)
        Или Документы.ТипВсеСсылки().СодержитТип(ТипЗнч)
        Или ПланыВидовХарактеристик.ТипВсеСсылки().СодержитТип(ТипЗнч);
КонецФункции

Как отличить ссылку от объекта по доступным свойствам

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

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

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

Ссылка или объект: что использовать

Ссылка

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

Объект

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

Проверка существования ссылки в базе

Отдельная задача — узнать, указывает ли ссылка на реально существующую запись. Так называемые «битые ссылки» появляются после удаления объектов без контроля ссылочной целостности, при обменах и при восстановлении данных. Признак битой ссылки в интерфейсе — отображение вида «<Объект не найден> (123:abc...)".

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

Запрос = Новый Запрос(

"ВЫБРАТЬ ПЕРВЫЕ 1 Истина

|ИЗ Справочник.Номенклатура

|ГДЕ Ссылка = &Ссылка");

Запрос.УстановитьПараметр("Ссылка", ПроверяемаяСсылка);

Если Запрос.Выполнить().Пустой() Тогда

// ссылка битая или запись удалена

КонецЕсли;

Ещё один лёгкий приём — обратиться к любому предопределённому реквизиту, например Ссылка.ПометкаУдаления, внутри попытки: у несуществующей записи значение будет Null. Этот способ удобен для одиночных проверок, но в массовых операциях лучше использовать запрос с группой ссылок.

Порядок проверки переменной
  1. 1
    Получите тип значения: ТипЗнч = ТипЗнч(Переменная).
  2. 2
    Сравните тип с нужным видом: Тип("СправочникСсылка.Номенклатура") или Тип("СправочникОбъект.Номенклатура").
  3. 3
    Если тип неизвестен заранее — проверьте вхождение в ТипВсеСсылки() соответствующего менеджера.
  4. 4
    Если это ссылка и нужно изменить данные — получите объект через ПолучитьОбъект() с проверкой на Неопределено.
  5. 5
    Проверьте результат: объект успешно создан, ЭтоНовый() и Модифицированность() ведут себя ожидаемо.

Получение объекта из ссылки и ссылки из объекта

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

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

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

Если Объект = Неопределено Тогда

Возврат; // записи нет в базе

КонецЕсли;

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

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

СсылкаНазад = Объект.Ссылка; // та же ссылка

Определение типа в запросах и на форме

В языке запросов тип значения проверяется оператором ССЫЛКА и функцией ТИПЗНАЧЕНИЯ(). Это полезно для полей составного типа, например реквизита «Контрагент», который может хранить и контрагента, и организацию: условие ГДЕ Реквизит ССЫЛКА Справочник.Контрагенты отберёт только нужный тип.

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

Для диагностики в пользовательском режиме без программирования тип значения виден в окне свойств элемента управления, а также через команду «Ещё — Изменить форму» при просмотре типов реквизитов. В отладчике достаточно добавить переменную в табло и посмотреть колонку «Тип».

Типичные ошибки и как их избежать

Самая распространённая ошибка — попытка изменить реквизит через ссылку: Ссылка.Наименование = "..." завершится ошибкой, потому что ссылка предназначена только для чтения. Вторая по частоте — сравнение ссылки и объекта оператором «=»: такое сравнение даёт ложь, даже если это одна и та же запись; сравнивать нужно Объект.Ссылка = Ссылка.

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

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

Часто задаваемые вопросы

Как быстро понять тип переменной без написания кода?

Откройте конфигуратор, поставьте точку останова рядом с нужной строкой, запустите отладку и добавьте переменную в табло (Shift+F9). Колонка «Тип» покажет точный тип значения, например «ДокументСсылка.РеализацияТоваровУслуг» или «ДокументОбъект.РеализацияТоваровУслуг».

Почему сравнение двух одинаковых записей даёт ложь?

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

Что такое «битая ссылка» и как её найти?

Это ссылка, указывающая на удалённую или никогда не существовавшую запись. В интерфейсе она отображается как «<Объект не найден>». Программно её выявляют запросом к таблице по ссылке: пустой результат означает, что записи нет. Для массового поиска используют типовые обработки поиска и удаления помеченных объектов с контролем ссылочной целостности.

Можно ли получить ссылку, не создавая объект?

Да. Метод Справочники.Имя.ПолучитьСсылку(УникальныйИдентификатор) создаёт значение ссылки по известному GUID без обращения к данным записи. Также ссылку на существующий элемент даёт НайтиПоКоду() или НайтиПоНаименованию() — они возвращают именно ссылку.

Как проверить, что значение вообще ссылочного типа, а не примитивное?

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

Почему ПолучитьОбъект() возвращает Неопределено?

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

Итог

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

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