Ссылка или объект в переменной: проверка типа значения во встроенном языке 1С
Содержание
Чтобы определить, объект или ссылка лежит в переменной, сравните тип значения с нужным типом: ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура") или ТипЗнч(Значение) = Тип("СправочникОбъект.Номенклатура"). Если конкретный справочник заранее неизвестен, используйте Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) для ссылок и XMLТип(ТипЗнч(Значение)).ИмяТипа для объектов.
Выбор способа зависит от задачи. Точное сравнение с Тип() подходит, когда вы знаете вид и имя метаданных. Проверка по коллекции ТипВсеСсылки() нужна в общих модулях, куда может прийти что угодно. Метаданные по типу (Метаданные.НайтиПоТипу) отвечают на вопрос «из какого справочника или документа это значение». Всё это работает в платформе 8.3 одинаково для любой конфигурации: БП, ЗУП, УТ или самописной.
Ссылка и объект: в чём разница для кода
Ссылка — это указатель на запись в базе. Объект — копия этой записи в памяти, которую можно менять и записывать. Отсюда все практические различия.
| Признак | Ссылка (СправочникСсылка.X) | Объект (СправочникОбъект.X) |
|---|---|---|
| Что хранит | Идентификатор записи | Все реквизиты и табличные части |
| Можно изменить | Нет | Да, затем Записать() |
| Где доступна | На клиенте и сервере | Только на сервере |
| Чтение реквизита через точку | Да, на сервере, с обращением к базе | Да, из памяти |
| Хранится в реквизите другого объекта | Да | Нет |
| Как получить другую сторону | Ссылка.ПолучитьОбъект() | Объект.Ссылка |
Несколько следствий, о которых чаще всего спотыкаются:
- 🔹 Обращение через точку у ссылки — это чтение из базы.
Ссылка.Наименованиев цикле по 10 000 строк — это до 10 000 обращений к данным. Для массовых чтений берите запрос илиОбщегоНазначения.ЗначенияРеквизитовОбъектаиз БСП. - 🔹
ПолучитьОбъект()читает запись целиком, со всеми табличными частями. Вызывать его ради одного реквизита — дорого. - 🔹 У нового объекта ссылка пустая до первой записи.
Объект.Ссылка.Пустая()вернётИстина, аОбъект.ЭтоНовый()— тожеИстина. - 🔹 Если запись удалена, ссылка остаётся «битой»:
Ссылка.ПолучитьОбъект()вернётНеопределено, а в форме вы увидите<Объект не найден>.
На клиенте в управляемом приложении объектов нет вовсе. Если код может выполняться и на клиенте, и на сервере, туда может прийти только ссылка или данные формы. Проверка «а вдруг это объект» на клиенте не нужна.
У перечислений объекта нет. Значение перечисления — это всегда ПеречислениеСсылка.ИмяПеречисления. Его сравнивают с элементом напрямую: Значение = Перечисления.СтавкиНДС.НДС20.
Функция ТипЗнч: как узнать тип значения переменной
ТипЗнч() возвращает значение типа Тип. Само по себе оно ничего не говорит: его сравнивают с другим типом, полученным через Тип().
Если ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") Тогда
// пришла ссылка на контрагента
ИначеЕсли ТипЗнч(Значение) = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
// пришёл объект документа
ИначеЕсли ТипЗнч(Значение) = Тип("Строка") Тогда
// пришла строка
КонецЕсли;
Строка в Тип() собирается по шаблону «ВидОбъектаСсылка.Имя» или «ВидОбъектаОбъект.Имя». Основные виды:
| Вид метаданных | Тип ссылки | Тип объекта |
|---|---|---|
| Справочник | СправочникСсылка.Имя | СправочникОбъект.Имя |
| Документ | ДокументСсылка.Имя | ДокументОбъект.Имя |
| Перечисление | ПеречислениеСсылка.Имя | — |
| План видов характеристик | ПланВидовХарактеристикСсылка.Имя | ПланВидовХарактеристикОбъект.Имя |
| План счетов | ПланСчетовСсылка.Имя | ПланСчетовОбъект.Имя |
| План обмена | ПланОбменаСсылка.Имя | ПланОбменаОбъект.Имя |
| Бизнес-процесс | БизнесПроцессСсылка.Имя | БизнесПроцессОбъект.Имя |
| Задача | ЗадачаСсылка.Имя | ЗадачаОбъект.Имя |
Для примитивов имена простые: Строка, Число, Дата, Булево, Неопределено, Null. Для универсальных коллекций — Массив, Структура, Соответствие, ТаблицаЗначений.
Если в Тип() указать имя, которого нет в конфигурации, код упадёт с ошибкой «Тип не определен». Это частая причина падений после переименования справочника или при переносе кода между конфигурациями. Опечатка в имени тоже ловится только при выполнении, а не при проверке модуля.
Не сравнивайте типы через строку: Строка(ТипЗнч(Значение)) = "Контрагенты". Строковое представление типа берётся из синонима, а синоним может поменяться при обновлении или зависеть от языка интерфейса.
Если одну и ту же проверку вы делаете в цикле, вынесите Тип() в переменную до цикла. Разбор строки с именем типа на каждой итерации — лишняя работа:
ТипКонтрагент = Тип("СправочникСсылка.Контрагенты");
Для Каждого Строка Из Таблица Цикл
Если ТипЗнч(Строка.Субконто1) = ТипКонтрагент Тогда
// ...
КонецЕсли;
КонецЦикла;
Как определить, ссылка это, объект или перечисление, если имя заранее неизвестно
В общих модулях, подписках на события и обработках конкретное имя справочника заранее не известно. Перебирать все типы вручную бессмысленно. Есть три надёжных приёма.
Приём 1. ТипВсеСсылки() — проверка на любую ссылку
У каждого менеджера коллекции есть метод ТипВсеСсылки(). Он возвращает ОписаниеТипов со всеми ссылочными типами этого вида:
Тип = ТипЗнч(Значение);
ЭтоСсылкаСправочника = Справочники.ТипВсеСсылки().СодержитТип(Тип);
ЭтоСсылкаДокумента = Документы.ТипВсеСсылки().СодержитТип(Тип);
ЭтоПеречисление = Перечисления.ТипВсеСсылки().СодержитТип(Тип);
Метод есть у Справочники, Документы, Перечисления, ПланыВидовХарактеристик, ПланыСчетов, ПланыВидовРасчета, ПланыОбмена, БизнесПроцессы, Задачи. Чтобы проверить «это вообще ссылка», объедините проверки через ИЛИ.
Приём 2. XMLТип() — проверка на объект по имени типа
Отдельного «ТипВсехОбъектов» в платформе нет. Зато у объектов есть XML-имя типа, по префиксу которого видно вид:
ТипXML = XMLТип(ТипЗнч(Значение));
Если ТипXML <> Неопределено Тогда
ИмяТипа = ТипXML.ИмяТипа; // например, "CatalogObject.Номенклатура"
ЭтоОбъектСправочника = СтрНачинаетсяС(ИмяТипа, "CatalogObject.");
ЭтоОбъектДокумента = СтрНачинаетсяС(ИмяТипа, "DocumentObject.");
ЭтоСсылкаСправочника = СтрНачинаетсяС(ИмяТипа, "CatalogRef.");
КонецЕсли;
Префиксы английские при любом языке конфигурации: CatalogRef, CatalogObject, DocumentRef, DocumentObject, EnumRef, ChartOfAccountsRef, ChartOfCharacteristicTypesRef, ExchangePlanRef и соответствующие ...Object. Для типов, у которых нет XML-представления (например, менеджер справочника), функция вернёт Неопределено — поэтому проверка на Неопределено перед .ИмяТипа обязательна.
Приём 3. Метаданные.НайтиПоТипу() — к какому объекту конфигурации относится значение
Если нужно имя справочника или документа, получите объект метаданных:
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(Значение));
Если ОбъектМетаданных <> Неопределено Тогда
ПолноеИмя = ОбъектМетаданных.ПолноеИмя(); // "Справочник.Номенклатура"
ЭтоДокумент = Метаданные.Документы.Содержит(ОбъектМетаданных);
КонецЕсли;
Для ссылки и для объекта одного справочника метод вернёт один и тот же объект метаданных. Поэтому НайтиПоТипу отвечает на вопрос «откуда значение», но не отличает ссылку от объекта. Для этого нужен приём 1 или 2. Для примитивов (Строка, Число) метод вернёт Неопределено.
Глобальный контекст Метаданные на клиенте недоступен. Приёмы 2 и 3 выполняйте на сервере. ТипЗнч() и Тип() работают в любом контексте.
Готовые функции БСП
В типовых конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11) писать эти проверки самому не нужно. В общем модуле ОбщегоНазначения есть:
- ✅
ОбщегоНазначения.ЭтоСсылка(Тип)— принимает тип, а не значение. ВозвращаетИстинадля любого ссылочного типа. - ✅
ОбщегоНазначения.ЗначениеСсылочногоТипа(Значение)— то же, но принимает само значение. - ✅
ОбщегоНазначения.ВидОбъектаПоСсылке(Ссылка)— вернёт строку вида «Справочник», «Документ». - ✅
ОбщегоНазначения.ЭтоСправочник(ОбъектМетаданных),ЭтоДокумент(...),ЭтоПеречисление(...)— принимают объект метаданных.
Частая ошибка — передать в ЭтоСсылка значение вместо типа. Пишите ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Значение)).
Определение типа в запросе: ССЫЛКА и ТИПЗНАЧЕНИЯ
Когда значение лежит в поле составного типа (регистратор, субконто, «Документ-основание»), проверять тип удобнее прямо в запросе, а не после выгрузки.
- 🔸 Оператор
ССЫЛКАпроверяет, что значение — ссылка на объект указанной таблицы:
ВЫБРАТЬ
Хозрасчетный.Регистратор
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
- 🔸 Функция
ТИПЗНАЧЕНИЯ()иТИП()нужны, когда тип выводится в результат или участвует в группировке:
ВЫБРАТЬ
ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) КАК ТипДокумента,
КОЛИЧЕСТВО(*) КАК Проводок
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
СГРУППИРОВАТЬ ПО
ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор)
Условие ТИПЗНАЧЕНИЯ(Поле) = ТИП(Документ.РеализацияТоваровУслуг) даёт тот же отбор, что ССЫЛКА. Запись через ССЫЛКА короче и читается легче.
Отдельный плюс ВЫРАЗИТЬ(Поле КАК Документ.РеализацияТоваровУслуг): при обращении к реквизитам поля составного типа через точку платформа соединяет все таблицы из состава типа. Явное приведение оставляет одну таблицу, и запрос работает заметно быстрее.
ОписаниеТипов: составные типы и приведение значений
ОписаниеТипов — это набор допустимых типов. Его возвращает ТипВсеСсылки(), оно же лежит в свойстве Тип любого реквизита в метаданных. Его можно создать и вручную.
// Строка с перечнем типов через запятую
ДопустимыеТипы = Новый ОписаниеТипов("СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица");
Если ДопустимыеТипы.СодержитТип(ТипЗнч(Значение)) Тогда
// значение подходит
КонецЕсли;
Основные методы:
| Метод | Что делает |
|---|---|
СодержитТип(Тип) | Проверяет, входит ли тип в описание |
Типы() | Возвращает массив всех типов описания |
ПривестиЗначение(Значение) | Приводит значение к описанию; если не подходит — вернёт значение по умолчанию |
Как узнать, какие типы допустимы в реквизите составного типа:
ОписаниеРеквизита = Метаданные.Документы.РеализацияТоваровУслуг.Реквизиты.ДокументОснование.Тип;
Для Каждого ДопустимыйТип Из ОписаниеРеквизита.Типы() Цикл
Сообщить(Метаданные.НайтиПоТипу(ДопустимыйТип).ПолноеИмя());
КонецЦикла;
Имя реквизита ДокументОснование здесь для примера: в вашей конфигурации подставьте реальный реквизит с составным типом. Если в составе есть примитивный тип, НайтиПоТипу вернёт для него Неопределено — в рабочем коде добавьте проверку.
ПривестиЗначение удобно использовать для безопасной загрузки из внешних источников. Новый ОписаниеТипов("Число").ПривестиЗначение("абв") вернёт 0, а не вызовет ошибку.
Неопределено, Null и пустая ссылка: три разных «пусто»
Три значения выглядят одинаково пустыми, но ведут себя по-разному. Перепутать их — классическая ошибка.
| Значение | Откуда берётся | Тип | Проверка |
|---|---|---|---|
Неопределено | Пустой реквизит составного типа, несуществующий ключ в Соответствии, необязательный параметр | Тип("Неопределено") | Значение = Неопределено |
Null | Поле запроса из левого соединения без пары, реквизит, которого нет у данного типа | Тип("Null") | Значение = Null |
| Пустая ссылка | Незаполненный реквизит ссылочного типа | Тип ссылки, например СправочникСсылка.Склады | Значение.Пустая() |
Что из этого следует:
- ⚠️
ТипЗнч(ПустаяСсылка)возвращает тип ссылки, а неНеопределено. ПроверкаТипЗнч(Склад) = Тип("СправочникСсылка.Склады")пройдёт и для пустого склада. - ⚠️ Вызов
.Пустая()уНеопределеноилиNullдаст ошибку «Метод объекта не обнаружен ». Сначала проверьте тип, потом пустоту. - ✅
ЗначениеЗаполнено(Значение)возвращаетЛожьдля всех трёх случаев, а также для0, пустой строки и пустой даты. Для вопроса «есть ли значение» этого достаточно.
Если реквизит составного типа, сначала проверяйте тип, потом обращайтесь к реквизитам значения. Иначе код упадёт на первой же строке, где в поле лежит строка или число.
В запросе Null обрабатывают функцией ЕСТЬNULL(Поле, ЗначениеПоУмолчанию) или условием Поле ЕСТЬ NULL. Сравнение Поле = NULL в языке запросов не работает
так, как ожидается.
Практические приёмы: функции на каждый день
Получить ссылку, что бы ни пришло
В общую процедуру может прийти и ссылка, и объект (например, из подписки на событие ПередЗаписью приходит объект, а из формы списка — ссылка). Функция ниже возвращает ссылку в обоих случаях. Выполнять на сервере:
Функция СсылкаПоЗначению(ОбъектИлиСсылка) Экспорт
Тип = ТипЗнч(ОбъектИлиСсылка);
Если ОбщегоНазначения.ЭтоСсылка(Тип) Тогда
Возврат ОбъектИлиСсылка;
КонецЕсли;
ТипXML = XMLТип(Тип);
Если ТипXML <> Неопределено
И СтрЗаканчиваетсяНа(СтрРазделить(ТипXML.ИмяТипа, ".")[0], "Object") Тогда
Возврат ОбъектИлиСсылка.Ссылка;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Если БСП в конфигурации нет, замените первую проверку на объединение ТипВсеСсылки() из приёма 1. Для нового незаписанного объекта функция вернёт пустую ссылку — учитывайте это в вызывающем коде.
Обработать поле составного типа без ошибок
Если ЗначениеЗаполнено(Основание)
И ТипЗнч(Основание) = Тип("ДокументСсылка.ЗаказКлиента") Тогда
Дата = Основание.Дата;
КонецЕсли;
Порядок условий важен: в 1С операторы И и ИЛИ вычисляются сокращённо. Если первое условие ложно, второе не проверяется.
Короткие правила
- 📌 Тип сравнивайте с
Тип(), а не со строкой. - 📌
Тип()с конкретным именем — для известных объектов,ТипВсеСсылки()— для любых. - 📌 Объект не таскайте между клиентом и сервером — передавайте ссылку.
- 📌 Объект ради одного реквизита не получайте: для этого есть запрос или чтение реквизитов через БСП.