Как узнать, что значение — ссылка на объект базы 1С
Содержание
Проверить ссылочный тип значения проще всего одной строкой: ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура"). Если нужно узнать не конкретный, а любой ссылочный тип, пригодятся метод Метаданные(), функция XMLТипЗнч() и операторы запроса ССЫЛКА и ВЫРАЗИТЬ. Ниже — каждый способ с рабочим кодом.
Ссылка в 1С — это указатель на объект базы: элемент справочника, документ, значение перечисления. У неё свой тип (СправочникСсылка.Контрагенты, ДокументСсылка.РеализацияТоваровУслуг), и именно по типу вы понимаете, с какой таблицей работаете. Путаница обычно начинается там, где в реквизит записан составной тип, и в одном поле лежат ссылки на разные объекты.
Разберём, как надёжно определить тип в коде, в запросе и прямо в отладчике, и чем пустая ссылка отличается от Неопределено.
Чем ссылочный тип отличается от объектного
В платформе есть две родственные сущности на один объект базы — ссылка и объект.
- 🔗 Ссылка (
СправочникСсылка.Валюты) — лёгкий неизменяемый указатель. Её хранят в реквизитах, передают в функции, кладут в регистры. Читать поля через ссылку можно, менять — нельзя. - 📦 Объект (
СправочникОбъект.Валюты) — «тяжёлая» запись целиком. Его получают методомПолучитьОбъект(), меняют реквизиты и записывают.
Это разные типы, хотя указывают на одну строку таблицы. Поэтому ТипЗнч(Ссылка) и ТипЗнч(Объект) вернут разные значения, и проверку надо писать под то, что реально лежит в переменной.
К ссылочным относятся типы справочников, документов, перечислений, планов видов характеристик, планов счетов, планов видов расчёта, бизнес-процессов и задач. У всех у них есть метод Метаданные() и общий предок — абстрактный тип «Ссылка». Именно это свойство мы и будем ловить.
Пустая ссылка (Справочники.Валюты.ПустаяСсылка()) — тоже значение ссылочного типа. Её ТипЗнч() равен СправочникСсылка.Валюты, но метод Пустая() вернёт Истина. Это не Неопределено.
Какой способ выбрать под вашу задачу
Способов несколько, и каждый закрывает свою ситуацию. Сверьтесь с таблицей, прежде чем писать код.
| Задача | Инструмент | Где работает |
|---|---|---|
| Проверить, что значение — ссылка на конкретный объект | ТипЗнч(Знач) = Тип("…") | код (клиент/сервер) |
| Узнать имя объекта у неизвестной ссылки | Знач.Метаданные().ПолноеИмя() | код, сервер |
| Поймать любой ссылочный тип | XMLТипЗнч(Знач) + поиск Ref. | код, сервер |
| Отобрать строки нужного типа в выборке | ССЫЛКА, ТИПЗНАЧЕНИЯ, ВЫРАЗИТЬ | язык запросов |
| Увидеть тип выполняющегося значения | Табло, «Вычислить выражение» | отладчик |
| Получить список типов составного поля | ОписаниеТипов.Типы() | код, сервер |
Правило простое: знаете ожидаемый тип — сравнивайте через Тип(); тип заранее неизвестен — идите через Метаданные() или XMLТипЗнч(); работаете в выборке — используйте операторы запроса. Эти варианты не конкурируют, а дополняют друг друга.
Проверка конкретного типа через ТипЗнч
Функция ТипЗнч(Значение) возвращает объект типа Тип. Сравнивать его надо не со строкой, а с результатом функции Тип():
Если ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура") Тогда
Сообщить("Это ссылка на номенклатуру");
КонецЕсли;
Строку с именем типа Тип() принимает в формате ВидОбъекта + Ссылка.ИмяОбъекта:
| Что проверяем | Строка для Тип() |
|---|---|
| Элемент справочника | СправочникСсылка.Контрагенты |
| Документ | ДокументСсылка.ПоступлениеТоваровУслуг |
| Значение перечисления | ПеречислениеСсылка.СтатусыЗаказов |
| Счёт плана счетов | ПланСчетовСсылка.Хозрасчетный |
| Вид характеристики | ПланВидовХарактеристикСсылка.ВидыСубконтоХозрасчетные |
Когда вариантов несколько, удобнее завести словарь типов заранее и не плодить ИначеЕсли:
ДопустимыеТипы = Новый Соответствие;
ДопустимыеТипы.Вставить(Тип("СправочникСсылка.Контрагенты"), "Контрагент");
ДопустимыеТипы.Вставить(Тип("СправочникСсылка.Организации"), "Организация");
Расшифровка = ДопустимыеТипы.Получить(ТипЗнч(Значение));
Если Расшифровка <> Неопределено Тогда
Сообщить("Найден: " + Расшифровка);
КонецЕсли;
Самая частая ошибка новичка — сравнение ТипЗнч(Значение) = "СправочникСсылка.Контрагенты". Слева тип, справа строка, равенство всегда ложно. Имя типа всегда оборачивайте в функцию Тип().
Как метод Метаданные() раскрывает имя объекта
Если тип заранее неизвестен, а надо узнать, какому объекту базы принадлежит ссылка, вызовите у неё Метаданные():
ОбъектМД = Значение.Метаданные();
Сообщить(ОбъектМД.ПолноеИмя()); // Справочник.Контрагенты
Сообщить(ОбъектМД.Имя); // Контрагенты
ПолноеИмя() возвращает строку вида Справочник.Контрагенты или Документ.РеализацияТоваровУслуг. По первому слову (до точки) видно вид объекта — справочник это, документ или регистр.
Сам объект метаданных тоже имеет тип, и по нему можно отличить справочник от документа без разбора строки:
Если ТипЗнч(Значение.Метаданные()) = Тип("ОбъектМетаданныхСправочник") Тогда
Сообщить("Ссылка указывает на справочник");
КонецЕсли;
Метод Метаданные() есть не у всех значений. Вызов на числе, строке или Неопределено вызовет ошибку. Поэтому в универсальной проверке его оборачивают в Попытка … Исключение или сначала отсеивают заведомо не-ссылки.
Учтите тонкость: метод Метаданные() есть и у ссылки, и у объекта. Для обоих Значение.Метаданные() вернёт один и тот же объект метаданных справочника. Если нужно именно отличить ссылку от объекта, одного Метаданные() мало — нужен способ из следующего раздела.
По имени из ПолноеИмя() удобно ветвить логику обработки. Первое слово строки (Справочник, Документ, ПланСчетов) задаёт вид объекта, второе — его имя в конфигурации:
ПолноеИмя = Значение.Метаданные().ПолноеИмя();
ВидОбъекта = СтрРазделить(ПолноеИмя, ".")[0]; // "Справочник"
Сообщить("Работаем с видом: " + ВидОбъекта);
Такой разбор не требует чтения базы — метаданные уже загружены в память платформы, обращение к ним мгновенно.
Универсальная функция «это ссылка или нет»
Чтобы поймать любой ссылочный тип и при этом не спутать ссылку с объектом, используйте XML-представление типа. У ссылочных значений имя XML-типа содержит Ref (CatalogRef.Валюты, DocumentRef.ПТУ), у объектов — Object (CatalogObject.Валюты). На этом и строим детектор:
Функция ЭтоСсылка(Знач Значение) Экспорт
Если Значение = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
ТипXML = XMLТипЗнч(Значение);
Если ТипXML = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
Возврат СтрНайти(ТипXML.ИмяТипа, "Ref.") > 0;
КонецФункции
Функция XMLТипЗнч(Значение) возвращает описание XML-типа; для числа, строки или даты имя типа будет decimal, string, dateTime, и проверка СтрНайти(..., "Ref.") отсечёт их. Для пустой ссылки функция честно вернёт Истина — пустая ссылка тоже ссылка.
Если в конфигурацию встроена БСП (Библиотека стандартных подсистем), в общем модуле ОбщегоНазначения есть готовая функция проверки ссылочного типа, которой передают тип значения. Точную сигнатуру удобно посмотреть прямо в модуле через контекстную подсказку — так вы не привяжетесь к неверному числу параметров.
Когда важно получить список всех ссылочных типов составного реквизита, разберите его описание типов:
ОписаниеТипов = Метаданные.Справочники.Договоры.Реквизиты.Владелец.Тип;
Для Каждого Тип Из ОписаниеТипов.Типы() Цикл
Сообщить(Строка(Тип));
КонецЦикла;
Метод .Типы() возвращает массив всех типов, объявленных у реквизита, а .СодержитТип(Тип("СправочникСсылка.Организации")) проверяет конкретный из них.
Определение типа в запросе
В языке запросов для работы с типом есть три инструмента. Все они нужны, когда поле хранит составной тип и в выборке лежат ссылки на разные объекты.
Оператор ССЫЛКА — логическая проверка принадлежности таблице:
ВЫБРАТЬ
Движения.Субконто1 КАК Субконто
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Движения
ГДЕ
Движения.Субконто1 ССЫЛКА Справочник.Контрагенты
Функция ТИПЗНАЧЕНИЯ возвращает тип поля, его сравнивают с функцией ТИП:
ГДЕ
ТИПЗНАЧЕНИЯ(Движения.Субконто1) = ТИП(Справочник.Контрагенты)
Функция ВЫРАЗИТЬ приводит составное поле к конкретному типу. Если значение другого типа — вернётся NULL, что тоже можно использовать как проверку:
ВЫБРАТЬ
ВЫРАЗИТЬ(Движения.Субконто1 КАК Справочник.Контрагенты).Наименование КАК Контрагент
ВЫРАЗИТЬ решает ещё одну задачу: без приведения вы не сможете обратиться к реквизитам объекта через точку, если поле составное. Платформа просто не знает, какие реквизиты у значения, пока вы не укажете тип явно.
В коллекциях — таблице значений, табличной части — тип столбца задаётся его описанием типов. Проверить, что́ столбец вообще может хранить, можно так:
ОписаниеТипов = Таблица.Колонки.Партнёр.ТипЗначения;
Если ОписаниеТипов.СодержитТип(Тип("СправочникСсылка.Контрагенты")) Тогда
Сообщить("Столбец принимает ссылку на контрагента");
КонецЕсли;
А конкретный тип значения в ячейке по-прежнему даёт ТипЗнч(Строка.Партнёр) — он покажет фактический тип, лежащий в этой строке, даже если столбец составной.
Пример: разбор составного субконто
Классическая ситуация — обход движений регистра бухгалтерии, где субконто хранит ссылки на разные справочники. Фактический тип каждой строки узнаём в коде:
Для Каждого Строка Из ТаблицаДвижений Цикл
ТекущийТип = ТипЗнч(Строка.Субконто1);
Если ТекущийТип = Тип("СправочникСсылка.Контрагенты") Тогда
ОбработатьКонтрагента(Строка.Субконто1);
ИначеЕсли ТекущийТип = Тип("СправочникСсылка.Договоры") Тогда
ОбработатьДоговор(Строка.Субконто1);
Иначе
// другой вид субконто — пропускаем
КонецЕсли;
КонецЦикла;
Здесь ТипЗнч() на каждой строке возвращает именно тот тип, который сейчас лежит в поле, а не весь список допустимых. Это главное отличие от ОписаниеТипов.Типы(), который отдаёт все объявленные варианты реквизита, независимо от конкретного значения.
Как посмотреть тип в отладчике
Когда код уже выполняется, а гадать о типе не хочется, остановите выполнение и посмотрите тип глазами.
- Поставьте точку останова на нужной строке (
F9). - Запустите в режиме отладки и дождитесь остановки.
- Откройте
Отладка → Таблои добавьте туда переменную. В колонке типа платформа сразу покажетСправочникСсылка.Контрагентыили другой тип. - Для разовой проверки удобнее
Отладка → Вычислить выражение: впишите тудаТипЗнч(Значение)и получите тип, аЗначение.Метаданные().ПолноеИмя()покажет имя объекта.
В табло можно добавлять не только переменные, но и выражения целиком: впишите ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") и смотрите Истина/Ложь на каждом шаге. Это быстрее, чем расставлять Сообщить() по коду и пересобирать.
Если навести курсор на переменную во время останова, всплывёт подсказка с текущим значением и типом — без добавления в табло.
Отладчик особенно выручает с составными полями: вы сразу видите, ссылка какого именно справочника пришла в конкретном прогоне, и не гадаете по коду. Если переменная оказалась Неопределено или пустой ссылкой, табло покажет это явно — и половина «плавающих» ошибок снимается ещё до правок.
Частые ошибки при определении типа
Ошибки здесь почти всегда одни и те же.
- ❌ Сравнение со строкой.
ТипЗнч(Знач) = "СправочникСсылка.Х"— всегда ложь. Нужна функцияТип(). - ❌ Вызов
Метаданные()на примитиве. Число, строка,Неопределенометода не имеют — будет ошибка выполнения. Сначала проверьте, что значение вообще ссылочное. - ❌ Путаница пустой ссылки и
Неопределено. Пустая ссылка — валидное значение со своим типом. ПроверкаЗначение = Неопределеноеё не поймает; для пустоты используйтеЗначение.Пустая(). - ❌ Игнорирование составного типа. В субконто, в поле «Владелец», в универсальных реквизитах лежат ссылки разных типов. Нельзя писать разбор под один тип — проверяйте каждый или приводите через
ВЫРАЗИТЬ. - ❌ Проверка объекта вместо ссылки. Если в переменной
СправочникОбъект, а вы сравниваете сСправочникСсылка, равенства не будет. Берите тип того, что реально пришло в процедуру. - ❌ Опечатка в имени типа.
Тип("СправочникСсылка.Контрагнты")не вызовет ошибку компиляции, но выбросит исключение в момент выполнения. Имя объекта проверяйте по дереву метаданных, а не по памяти.
Отдельно про производительность: в запросах по большим регистрам фильтр ТИПЗНАЧЕНИЯ(...) = ТИП(...) работает лучше, чем выгрузка в код и перебор ТипЗнч() по каждой строке. Отбор по типу платформа умеет считать на стороне СУБД.
Чем `ТипЗнч()` отличается от `XMLТипЗнч()`?
ТипЗнч() возвращает объект Тип для сравнения в коде. XMLТипЗнч() возвращает описание XML-типа с текстовым именем (CatalogRef.Валюты) — его удобно разбирать строкой, когда нужен универсальный детектор ссылок.Как одной проверкой отличить справочник от документа?
ТипЗнч(Значение.Метаданные()) = Тип("ОбъектМетаданныхСправочник"). Для документа — ОбъектМетаданныхДокумент. Не придётся разбирать строку ПолноеИмя().Почему `ВЫРАЗИТЬ` в запросе возвращает NULL?
ВЫРАЗИТЬ вернёт заполненное значение только для «своего» типа, а для остальных — NULL.Можно ли узнать тип, не обращаясь к базе?
ТипЗнч(), XMLТипЗнч() и разбор ОписаниеТипов работают с самим значением и его метаданными, без запроса к СУБД. Обращение к базе идёт только при чтении реквизитов через ссылку.Как получить все ссылочные типы конкретного реквизита?
ОписаниеТипов.Типы(). Метод .СодержитТип() проверит наличие конкретного типа в составе.