В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда при сравнении двух казалось бы одинаковых элементов справочника система выдает неожиданный результат. Рассмотрим типичный сценарий: программист сравнивает две переменные, содержащие ссылку на одну и ту же номенклатуру. При прямом сравнении Ссылка1 = Ссылка2 получается Ложь, однако при сравнении их идентификаторов Ссылка1.УникальныйИдентификатор() = Ссылка2.УникальныйИдентификатор() результат становится Истина. В этой статье мы подробно проанализируем, почему так происходит, и разберем механизмы работы платформы со ссылочными данными.
Для начала выясним, что представляет собой каждый из этих типов данных. СправочникСсылка — это сложный объект, который содержит в себе не только 128-битный глобальный уникальный идентификатор (GUID), но и информацию о типе объекта (метаданных). Платформа использует эту информацию для понимания того, в какой таблице базы данных искать объект.
Метод УникальныйИдентификатор(), в свою очередь, возвращает объект типа УникальныйИдентификатор. Это «чистый» GUID без привязки к конкретной таблице метаданных. Когда мы сравниваем два UUID, система просто побитово сопоставляет две последовательности цифр. Когда же мы сравниваем две ссылки, 1С проверяет их полное соответствие, включая внутренний идентификатор типа.
Одной из наиболее вероятных причин описываемой проблемы является использование COM-соединения. Если одна ссылка получена из текущей информационной базы, а вторая — через COMОбъект из другой базы (или даже из этой же, но через внешний интерфейс), прямое сравнение всегда будет возвращать Ложь.
Проанализируем ситуацию: для текущей сессии объект, полученный через COM, имеет тип COMObject. Несмотря на то что визуально в отладчике он может выглядеть как ссылка и даже иметь строковое представление названия номенклатуры, для ядра 1С это разные типы данных. Рассмотрим пример кода, который часто приводит к ошибке:
// СсылкаИзБазы — родная ссылка текущей базы
// СсылкаИзCOM — ссылка, полученная через COM-соединение
Если СсылкаИзБазы = СсылкаИзCOM Тогда
// Этот блок никогда не выполнится, так как типы различаются
КонецЕсли;
Если СсылкаИзБазы.УникальныйИдентификатор() = СсылкаИзCOM.УникальныйИдентификатор() Тогда
// А здесь может быть Истина, если COM-объект корректно транслирует метод
КонецЕсли;
В случае с COM-соединением наиболее надежным способом сравнения будет приведение обоих идентификаторов к строковому виду. Это гарантирует, что мы сравниваем примитивные типы данных (строки), а не сложные объекты разных сред.
Другой важный аспект — наличие в базе данных так называемых «битых» ссылок (в интерфейсе они отображаются как «<Объект не найден>...»). Такая ссылка физически существует, она содержит UUID, но записи с таким идентификатором в соответствующей таблице базы данных нет.
Выясним причину странного поведения при сравнении таких ссылок. В 1С сравнение NULL = NULL всегда дает Ложь (согласно логике трехзначной логики SQL). Хотя «битая» ссылка — это не совсем NULL, при определенных манипуляциях с Таблицей Значений или при выгрузке данных без явного указания типов, платформа может обрабатывать их специфическим образом. Если одна из ссылок в вашей таблице значений не была типизирована должным образом, результат сравнения может быть непредсказуемым.
Иногда программисты сталкиваются с тем, что идентификаторы (UUID) совпадают, но ссылки относятся к разным типам метаданных. Это крайне редкая ситуация для справочника «Номенклатура», но она возможна при использовании планов обмена или прямой записи в базу данных через SQL. Ссылка в 1С — это пара (ТипОбъекта, UUID). Если типы разные (например, один объект — это «Номенклатура», а другой — случайно созданный элемент в другом справочнике с тем же GUID), сравнение ссылок даст Ложь, а сравнение UUID — Истина.
Посмотрим на пример кода автора темы. Проблема возникает при обходе ТаблицыЗначений, полученной путем выгрузки из табличной части документа. Рассмотрим, как сделать проверку на дубли или совпадения более надежной:
ВыборкаДетальныеЗаписи = СсылкаНаДок.Товары.Выгрузить();
Для Каждого СтрСтараяТЗВнеш Из ВыборкаДетальныеЗаписи Цикл
// Используем строковое представление для гарантированного результата
УИД_Внешний = Строка(СтрСтараяТЗВнеш.Номенклатура.УникальныйИдентификатор());
Для Каждого СтрВнутр Из ВыборкаДетальныеЗаписи Цикл
Если СтрСтараяТЗВнеш = СтрВнутр Тогда Продолжить; КонецЕсли;
УИД_Внутренний = Строка(СтрВнутр.Номенклатура.УникальныйИдентификатор());
Если УИД_Внешний = УИД_Внутренний Тогда
Сообщить("Найден дубль номенклатуры по GUID!");
КонецЕсли;
КонецЦикла;
КонецЦикла;
Использование функции Строка() от УникальныйИдентификатор() — это «золотой стандарт» при работе в условиях неопределенности типов (например, при обмене данными между базами).
Подводя итог нашему анализу, выделим основные правила, которые помогут избежать ошибок при сравнении ссылок:
СправочникСсылка.Номенклатура) при записи в ячейку.УникальныйИдентификатор — это тоже объект. В некоторых случаях (особенно в старых версиях платформы или при работе с COM) даже два объекта UUID могут не совпасть, если они получены из разных источников. Самый надежный способ — сравнение строк.Таким образом, разница между Номенклатура и Номенклатура.УникальныйИдентификатор() заключается в объеме информации, которую анализирует платформа. Ссылка — это контекстный объект базы данных, а идентификатор — лишь его технический адрес. Понимание этой разницы позволяет писать более отказоустойчивый код и избегать трудноуловимых ошибок при обработке больших массивов данных.