Как узнать, что значение — ссылка на объект базы 1С

· 9 мин

Содержание

Проверить ссылочный тип значения проще всего одной строкой: ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура"). Если нужно узнать не конкретный, а любой ссылочный тип, пригодятся метод Метаданные(), функция 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);
    Иначе
        // другой вид субконто — пропускаем
    КонецЕсли;
КонецЦикла;

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

Как посмотреть тип в отладчике

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

  1. Поставьте точку останова на нужной строке (F9).
  2. Запустите в режиме отладки и дождитесь остановки.
  3. Откройте Отладка → Табло и добавьте туда переменную. В колонке типа платформа сразу покажет СправочникСсылка.Контрагенты или другой тип.
  4. Для разовой проверки удобнее Отладка → Вычислить выражение: впишите туда ТипЗнч(Значение) и получите тип, а Значение.Метаданные().ПолноеИмя() покажет имя объекта.

В табло можно добавлять не только переменные, но и выражения целиком: впишите ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") и смотрите Истина/Ложь на каждом шаге. Это быстрее, чем расставлять Сообщить() по коду и пересобирать.

Если навести курсор на переменную во время останова, всплывёт подсказка с текущим значением и типом — без добавления в табло.

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

Проверьте себя
Чему равно `ТипЗнч(Справочники.Валюты.ПустаяСсылка())`?
Как правильно сравнить тип значения с типом справочника в коде?
Какой оператор запроса проверяет принадлежность поля конкретной таблице?

Частые ошибки при определении типа

Ошибки здесь почти всегда одни и те же.

  • ❌ Сравнение со строкой. ТипЗнч(Знач) = "СправочникСсылка.Х" — всегда ложь. Нужна функция Тип().
  • ❌ Вызов Метаданные() на примитиве. Число, строка, Неопределено метода не имеют — будет ошибка выполнения. Сначала проверьте, что значение вообще ссылочное.
  • ❌ Путаница пустой ссылки и Неопределено. Пустая ссылка — валидное значение со своим типом. Проверка Значение = Неопределено её не поймает; для пустоты используйте Значение.Пустая().
  • ❌ Игнорирование составного типа. В субконто, в поле «Владелец», в универсальных реквизитах лежат ссылки разных типов. Нельзя писать разбор под один тип — проверяйте каждый или приводите через ВЫРАЗИТЬ.
  • ❌ Проверка объекта вместо ссылки. Если в переменной СправочникОбъект, а вы сравниваете с СправочникСсылка, равенства не будет. Берите тип того, что реально пришло в процедуру.
  • ❌ Опечатка в имени типа. Тип("СправочникСсылка.Контрагнты") не вызовет ошибку компиляции, но выбросит исключение в момент выполнения. Имя объекта проверяйте по дереву метаданных, а не по памяти.

Отдельно про производительность: в запросах по большим регистрам фильтр ТИПЗНАЧЕНИЯ(...) = ТИП(...) работает лучше, чем выгрузка в код и перебор ТипЗнч() по каждой строке. Отбор по типу платформа умеет считать на стороне СУБД.

Чем `ТипЗнч()` отличается от `XMLТипЗнч()`?
ТипЗнч() возвращает объект Тип для сравнения в коде. XMLТипЗнч() возвращает описание XML-типа с текстовым именем (CatalogRef.Валюты) — его удобно разбирать строкой, когда нужен универсальный детектор ссылок.
Как одной проверкой отличить справочник от документа?
Сравните тип объекта метаданных: ТипЗнч(Значение.Метаданные()) = Тип("ОбъектМетаданныхСправочник"). Для документа — ОбъектМетаданныхДокумент. Не придётся разбирать строку ПолноеИмя().
Почему `ВЫРАЗИТЬ` в запросе возвращает NULL?
Значит, фактический тип значения в поле не совпал с типом, к которому вы приводите. Для составного поля это нормально: ВЫРАЗИТЬ вернёт заполненное значение только для «своего» типа, а для остальных — NULL.
Можно ли узнать тип, не обращаясь к базе?
Да. ТипЗнч(), XMLТипЗнч() и разбор ОписаниеТипов работают с самим значением и его метаданными, без запроса к СУБД. Обращение к базе идёт только при чтении реквизитов через ссылку.
Как получить все ссылочные типы конкретного реквизита?
Возьмите описание типов реквизита из метаданных и переберите ОписаниеТипов.Типы(). Метод .СодержитТип() проверит наличие конкретного типа в составе.