Как работает сравнение объектов в 1С: почему «Новый» не всегда создает уникальное значение?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В повседневной разработке на платформе 1С:Предприятие мы часто используем оператор сравнения =. Казалось бы, всё просто: примитивные типы (числа, строки) сравниваются по значению, а объекты (ссылки, структуры, таблицы) — по ссылке. Однако, если мы начнем сравнивать объекты, созданные через конструктор Новый, результаты могут поставить в тупик даже опытного разработчика. Рассмотрим, почему в одних случаях два новых объекта равны, а в других — нет, и как это знание применить на практике.

Основные механизмы сравнения: Ссылка против Значения

Для того чтобы понять логику платформы, мы должны заглянуть «под капот». В языке 1С для каждого типа данных на уровне ядра (написанного на C++) реализован свой алгоритм сравнения. Существует две основные стратегии:

  1. Сравнение по ссылке (Reference Equality): Платформа проверяет, указывают ли две переменные на одну и тот же область памяти. Если вы создали два объекта Структура, даже с идентичными ключами и значениями, они не будут равны, так как это разные экземпляры.
  2. Сравнение по значению (Value Equality): Платформа анализирует внутреннее состояние объекта. Если свойства объектов идентичны, результат будет Истина. Это часто применяется для «объектов-описателей».
  3. Кэширование и мемоизация: В некоторых случаях платформа не создает новый объект в памяти, а возвращает ссылку на уже существующий (синглтон или кэшированный экземпляр), если параметры конструктора совпадают.

Проанализируем конкретные примеры из «гуру-теста» и выясним причины такого поведения.

Разбор объектов, сравниваемых по значению

Рассмотрим ситуацию, когда конструктор Новый возвращает значения, которые при сравнении дают Истина. Проанализируем следующий код:


ОписаниеТипов1 = Новый ОписаниеТипов("Число, Строка") = Новый ОписаниеТипов("Число, Строка"); // Истина
ОписаниеТипов2 = Новый ОписаниеТипов("Строка, Число") = Новый ОписаниеТипов("Число, Строка"); // Истина
КвалификаторыЧисла = Новый КвалификаторыЧисла(10, 2) = Новый КвалификаторыЧисла(10, 2); // Истина

Почему это происходит? Рассмотрим объект ОписаниеТипов. При его создании платформа выполняет внутреннюю нормализацию: типы сортируются, дубликаты удаляются. Более того, 1С активно использует кэширование описаний типов для оптимизации работы с базой данных. В результате, даже если вы поменяли местами «Строка» и «Число» в строке конструктора, внутреннее состояние объектов будет идентичным, и платформа вернет Истина.

Аналогично ведут себя КвалификаторыЧисла, КвалификаторыСтроки и КвалификаторыДаты. Это неизменяемые (immutable) объекты, назначение которых — просто описывать параметры данных. Хранить в памяти тысячи одинаковых описателей бессмысленно, поэтому система сравнивает их по содержимому.

Специфические объекты: Параметры записи XML и JSON

Многих удивляет поведение объектов для работы с XML и JSON. Проверим:


ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML; // Истина

Несмотря на то что это объекты с множеством свойств (кодировка, отступы и т.д.), платформа считает их равными, если настройки совпадают. Это происходит потому, что такие объекты часто используются в механизмах сериализации. Для системы важно быстро определить, отличаются ли настройки записи, чтобы понимать, нужно ли перестраивать поток вывода.

Объекты, сравниваемые по ссылке (уникальные экземпляры)

Теперь разберем случаи, когда Новый всегда порождает уникальный, не равный другому объект:


Структура = Новый Структура("Ключ") = Новый Структура("Ключ"); // Ложь
СжатиеДанных = Новый СжатиеДанных(9) = Новый СжатиеДанных(9); // Ложь
ОписаниеОповещения = Новый ОписаниеОповещения("Имя", ЭтотОбъект) = Новый ОписаниеОповещения("Имя", ЭтотОбъект); // Ложь

Структура и Массив: Это мутабельные коллекции. Вы можете изменить их содержимое в любой момент. Если бы платформа сравнивала их по значению, ей пришлось бы при каждом сравнении обходить все элементы коллекции, что крайне непроизводительно. Поэтому для них всегда используется сравнение указателей в памяти.

СжатиеДанных: Это интересный случай. Несмотря на то что у объекта всего один параметр (уровень сжатия), разработчики платформы не реализовали для него сравнение по значению. Вероятно, это связано с тем, что объект является оберткой над низкоуровневыми библиотеками сжатия, и каждый экземпляр управляет своим состоянием потока.

Особенности работы с Метаданными

Работа с метаданными в 1С подчиняется правилу «единственного экземпляра» (Singleton). Выясним, почему это так, на примере:


Ф1 = Метаданные = Метаданные; // Истина
Ф3 = Метаданные.Справочники.Валюты = Метаданные.Справочники.Валюты; // Истина
Ф4 = Метаданные.Справочники.Валюты = Метаданные.Справочники.Валюты.Реквизиты.Наименование.Родитель(); // Истина

Платформа загружает дерево метаданных конфигурации в память один раз при запуске сеанса. Когда вы обращаетесь к Метаданные.Справочники.Валюты, вы получаете указатель на конкретный узел этого дерева. Любой другой путь, приводящий к этому же узлу (например, через метод Родитель() подчиненного реквизита), вернет тот же самый указатель. Поэтому сравнение объектов метаданных всегда надежно и быстро.

Коварный Табличный Документ: Область Ячеек

Одной из самых неожиданных находок является сравнение областей табличного документа. Проанализируем ситуацию:

Результат = ТабДок.Область(1, 1) = ТабДок.Область(1, 1); // Ложь!

Даже если координаты области (Y1, X1, Y2, X2) абсолютно одинаковы, результат будет Ложь. Это происходит потому, что метод Область() каждый раз создает новый объект-прокси для манипуляции ячейками. Платформа не выполняет «канонизацию» этих объектов. Если вам нужно проверить, являются ли две переменные одной и той же областью, сравнивайте их свойства Имя или координаты вручную.

Полный список «кэшируемых» объектов

На основе тестов и анализа поведения платформы, мы можем составить список типов, для которых сравнение Новый Тип(...) = Новый Тип(...) с большой вероятностью вернет Истина:

Почему это важно для практики?

Самый важный аспект применения этих знаний — использование объектов в качестве ключей в коллекции Соответствие.

Вспомним: поиск в Соответствие работает по тем же правилам, что и оператор =. Если вы используете в качестве ключа ОписаниеТипов, вы сможете получить значение из соответствия, передав туда «новый» объект с тем же составом типов. Но если вы используете в качестве ключа Структуру или СжатиеДанных, вы никогда не найдете значение по новому экземпляру, так как они не будут равны (ссылки разные).

Подведем итог: Не полагайтесь на интуицию при сравнении объектов. Если объект сложный (коллекция или прокси-объект вроде области таблицы), сравнивайте его по ключевым идентификаторам или свойствам. Если объект описывает тип или формат — смело используйте прямое сравнение.

← На главную