Разработка и Конфигурация · 12 мин
Как узнать в 1С: объект или ссылка
Как определить в 1С, является ли переменная объектом или ссылкой: методы ТипЗначения, СсылкаСуществует, НайтиПоСсылкам, примеры кода и типичные ошибки.
В языке 1С одна и та же запись базы данных может быть представлена двумя разными значениями: ссылкой и объектом. Ссылка — это лёгкий идентификатор, указывающий на запись в базе, а объект — загруженная в память копия данных с возможностью изменения и записи. Путаница между ними — одна из самых частых причин ошибок вида «Поле объекта не обнаружено» и неожиданных обращений к базе данных.
Определить, что именно находится в переменной, можно несколькими способами: через функцию ТипЗначения(), через сравнение типа с нужным видом объекта метаданных, через проверку свойств и через серию тестовых обращений в отладчике. Выбор способа зависит от того, где выполняется проверка — на сервере, на клиенте, в запросе или в отладочном коде.
Ниже разобраны практические приёмы: как программно отличить ссылку от объекта, как проверить существование ссылки в базе, как получить объект из ссылки и обратно, а также типичные ошибки при таких проверках и способы их избежать.
Чем ссылка отличается от объекта в 1С
Ссылка — это значение, которое содержит только уникальный идентификатор записи (UUID) и указание на таблицу, где хранится запись. Сама по себе ссылка не содержит реквизитов элемента: чтобы прочитать, например, наименование или код, платформа неявно обращается к базе данных. Именно поэтому обращение к реквизитам «через точку» от ссылки вызывает скрытый запрос, что критично для производительности в циклах.
Объект — это полноценная копия данных в памяти, загруженная из базы или созданная заново (например, методом СоздатьЭлемент()). У объекта доступны все реквизиты для чтения и изменения, методы Записать(), Удалить(), свойство ЭтоНовый(), обработчики событий ПередЗаписью и ПриЗаписи. После записи объект сам предоставляет ссылку через свойство Ссылка.
Ключевое практическое следствие: ссылки дёшево передавать между сервером и клиентом и хранить в реквизитах других объектов, а объекты нужны только тогда, когда данные требуется изменить и записать. Избыточное использование объектов замедляет работу и повышает риск конфликтов блокировок.
Определяем тип значения через ТипЗначения
Универсальный способ узнать, что лежит в переменной, — функция ТипЗначения(Значение). Она возвращает объект типа Тип, который можно сравнить с конкретным типом через функцию Тип("ИмяТипа"). Для справочника «Номенклатура» ссылка будет иметь тип СправочникСсылка.Номенклатура, а объект — СправочникОбъект.Номенклатура.
ТипЗнч = ТипЗнч(Переменная);
Если ТипЗнч = Тип("СправочникСсылка.Номенклатура") Тогда
// это ссылка
ИначеЕсли ТипЗнч = Тип("СправочникОбъект.Номенклатура") Тогда
// это объект
КонецЕсли;
Если нужно проверить принадлежность не к конкретному виду, а к целой группе (любая ссылка справочника, любая ссылка документа), используют обобщённые типы: СправочникСсылка, ДокументСсылка, ПеречислениеСсылка. Проверка «это вообще ссылка или нет» часто выполняется через Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч) в сочетании с аналогичными проверками для документов, планов видов характеристик и других ссылочных типов.
// Универсальная проверка: является ли значение любой ссылкой
Функция ЭтоСсылка(Значение)
ТипЗнч = ТипЗнч(Значение);
Возврат Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч)
Или Документы.ТипВсеСсылки().СодержитТип(ТипЗнч)
Или ПланыВидовХарактеристик.ТипВсеСсылки().СодержитТип(ТипЗнч);
КонецФункцииКак отличить ссылку от объекта по доступным свойствам
Если писать универсальный код, который должен работать и со ссылками, и с объектами, удобно опираться на различия в их интерфейсе. У объекта есть методы Записать(), Модифицированность(), ЭтоНовый(), а у ссылки их нет. У ссылки нет табличных частей для изменения — попытка обратиться к ним вернёт данные только для чтения через неявный запрос.
Надёжный приём — попытаться получить объект через ПолучитьОбъект(): у ссылки этот метод есть и он вернёт объект, а у объекта такого метода нет. Однако вызывать ПолучитьОбъект() только ради проверки типа не стоит — это дорогая операция, загружающая всю запись из базы. Для проверки типа всегда предпочтительнее ТипЗнч().
В отладчике различие видно сразу: ссылка отображается с типом вида «СправочникСсылка.Номенклатура» и при разворачивании показывает реквизиты, подгружаемые по требованию, а объект имеет тип «СправочникОбъект.Номенклатура» и содержит полный набор свойств, включая служебные (ДополнительныеСвойства, ОбменДанными).
Ссылка или объект: что использовать
Подходит для хранения в реквизитах, передачи между клиентом и сервером, сравнения и поиска. Ограничение: нельзя изменить данные, чтение реквизитов вызывает обращение к базе.
Нужен для создания, изменения и записи данных, доступа к обработчикам событий. Ограничение: дорогая загрузка, нельзя передать на тонкий клиент, риск блокировок при длительном удержании.
Проверка существования ссылки в базе
Отдельная задача — узнать, указывает ли ссылка на реально существующую запись. Так называемые «битые ссылки» появляются после удаления объектов без контроля ссылочной целостности, при обменах и при восстановлении данных. Признак битой ссылки в интерфейсе — отображение вида «<Объект не найден> (123:abc...)".
Программно проверить существование можно несколькими способами. Самый быстрый — запрос к базе: если запрос по ссылке вернул пустую выборку, записи нет. Альтернатива без запроса — проверка через Ссылка.ПолучитьОбъект(): для несуществующей записи метод вернёт Неопределено, но это медленнее, так как платформа пытается загрузить данные целиком.
Запрос = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1 Истина
|ИЗ Справочник.Номенклатура
|ГДЕ Ссылка = &Ссылка");
Запрос.УстановитьПараметр("Ссылка", ПроверяемаяСсылка);
Если Запрос.Выполнить().Пустой() Тогда
// ссылка битая или запись удалена
КонецЕсли;
Ещё один лёгкий приём — обратиться к любому предопределённому реквизиту, например Ссылка.ПометкаУдаления, внутри попытки: у несуществующей записи значение будет Null. Этот способ удобен для одиночных проверок, но в массовых операциях лучше использовать запрос с группой ссылок.
- 1Получите тип значения: ТипЗнч = ТипЗнч(Переменная).
- 2Сравните тип с нужным видом: Тип("СправочникСсылка.Номенклатура") или Тип("СправочникОбъект.Номенклатура").
- 3Если тип неизвестен заранее — проверьте вхождение в ТипВсеСсылки() соответствующего менеджера.
- 4Если это ссылка и нужно изменить данные — получите объект через ПолучитьОбъект() с проверкой на Неопределено.
- 5Проверьте результат: объект успешно создан, ЭтоНовый() и Модифицированность() ведут себя ожидаемо.
Получение объекта из ссылки и ссылки из объекта
Переход от ссылки к объекту выполняется методом ПолучитьОбъект(). Он загружает запись из базы в память, и дальше с данными можно работать полноценно: менять реквизиты, заполнять табличные части, вызывать Записать(). Если запись отсутствует, метод вернёт Неопределено — это нужно обрабатывать всегда, иначе следующее обращение вызовет ошибку.
Обратный переход тривиален: у любого объекта есть свойство Ссылка. Для нового, ещё не записанного объекта ссылка уже существует (она генерируется при создании), либо её можно задать явно через УстановитьСсылкуНового() — это важно при обменах, когда нужно сохранить идентичность ссылок между базами.
Объект = Ссылка.ПолучитьОбъект();
Если Объект = Неопределено Тогда
Возврат; // записи нет в базе
КонецЕсли;
Объект.Наименование = "Новое наименование";
Объект.Записать();
СсылкаНазад = Объект.Ссылка; // та же ссылка
Определение типа в запросах и на форме
В языке запросов тип значения проверяется оператором ССЫЛКА и функцией ТИПЗНАЧЕНИЯ(). Это полезно для полей составного типа, например реквизита «Контрагент», который может хранить и контрагента, и организацию: условие ГДЕ Реквизит ССЫЛКА Справочник.Контрагенты отберёт только нужный тип.
На управляемой форме ситуация проще: реквизиты формы ссылочного типа всегда содержат именно ссылки, потому что объекты нельзя передавать между клиентом и сервером. Если в модуле формы требуется объект, его получают в серверной процедуре через РеквизитФормыВЗначение() (для основного реквизита объекта) или через Ссылка.ПолучитьОбъект() в серверном контексте.
Для диагностики в пользовательском режиме без программирования тип значения виден в окне свойств элемента управления, а также через команду «Ещё — Изменить форму» при просмотре типов реквизитов. В отладчике достаточно добавить переменную в табло и посмотреть колонку «Тип».
Типичные ошибки и как их избежать
Самая распространённая ошибка — попытка изменить реквизит через ссылку: Ссылка.Наименование = "..." завершится ошибкой, потому что ссылка предназначена только для чтения. Вторая по частоте — сравнение ссылки и объекта оператором «=»: такое сравнение даёт ложь, даже если это одна и та же запись; сравнивать нужно Объект.Ссылка = Ссылка.
Также разработчики часто забывают проверять результат ПолучитьОбъект() на Неопределено и ловят ошибку на следующей строке, а ещё — вызывают Записать() у объекта, полученного из ссылки, не проверив Модифицированность(), что приводит к лишним записям и срабатыванию подписок на события.
Часто задаваемые вопросы
Как быстро понять тип переменной без написания кода?
Откройте конфигуратор, поставьте точку останова рядом с нужной строкой, запустите отладку и добавьте переменную в табло (Shift+F9). Колонка «Тип» покажет точный тип значения, например «ДокументСсылка.РеализацияТоваровУслуг» или «ДокументОбъект.РеализацияТоваровУслуг».
Почему сравнение двух одинаковых записей даёт ложь?
Скорее всего, вы сравниваете объект со ссылкой. Оператор равенства сравнивает и типы значений: объект никогда не равен ссылке, даже если они указывают на одну запись. Сравнивайте Объект.Ссылка = Ссылка или приводите оба значения к ссылкам.
Что такое «битая ссылка» и как её найти?
Это ссылка, указывающая на удалённую или никогда не существовавшую запись. В интерфейсе она отображается как «<Объект не найден>». Программно её выявляют запросом к таблице по ссылке: пустой результат означает, что записи нет. Для массового поиска используют типовые обработки поиска и удаления помеченных объектов с контролем ссылочной целостности.
Можно ли получить ссылку, не создавая объект?
Да. Метод Справочники.Имя.ПолучитьСсылку(УникальныйИдентификатор) создаёт значение ссылки по известному GUID без обращения к данным записи. Также ссылку на существующий элемент даёт НайтиПоКоду() или НайтиПоНаименованию() — они возвращают именно ссылку.
Как проверить, что значение вообще ссылочного типа, а не примитивное?
Используйте комбинацию проверок: если ТипЗнч(Значение) содержится в Справочники.ТипВсеСсылки(), Документы.ТипВсеСсылки() или аналогичных описаниях других менеджеров — значение является ссылкой. Примитивные типы (строка, число, дата, булево) в эти описания не входят.
Почему ПолучитьОбъект() возвращает Неопределено?
Метод возвращает Неопределено, когда запись с таким идентификатором отсутствует в таблице: она удалена, либо ссылка сформирована искусственно через ПолучитьСсылку() для ещё не созданного элемента. Всегда проверяйте результат перед обращением к реквизитам объекта.
Что важно запомнить
Ссылка — лёгкий указатель на запись, объект — загруженные данные для изменения. Тип определяется через ТипЗнч() и сравнение с «ВидСсылка.Имя» или «ВидОбъект.Имя». Объект получают из ссылки методом ПолучитьОбъект() с обязательной проверкой на Неопределено, ссылку из объекта — через свойство Ссылка. В циклах и на клиенте объектные операции заменяйте запросами и серверными вызовами.