Как 1С сравнивает объекты типа "Шрифт" и другие неизменяемые типы?

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

При работе с платформой 1С мы можем столкнуться с неочевидным поведением при сравнении объектов различных типов. Один из таких случаев касается сравнения экземпляров типа Шрифт, который часто вызывает вопросы у разработчиков. Давайте вместе разберем, почему сравнение двух, казалось бы, одинаково созданных объектов Новый Шрифт("Arial") может возвращать Истина, в то время как аналогичное сравнение для Новый Структура() всегда дает Ложь.

Мы проанализируем эту ситуацию, углубимся в концепцию неизменяемых типов данных в 1С, рассмотрим влияние кэширования конструкторов и продемонстрируем, как сравнение происходит по значению, а не по ссылке, даже при различных способах создания объектов.

Особенности сравнения неизменяемых типов данных в 1С

Для начала, давайте разберемся с ключевым понятием: неизменяемые (иммутабельные) типы данных. В 1С существуют типы объектов, чьи свойства не могут быть изменены после их создания. Примерами таких типов являются Шрифт, ОписаниеТипов, Дата, Число, Строка и другие. Это означает, что если мы создаем объект типа Шрифт с определенным набором свойств (например, "Arial", размер 10, жирный), то эти свойства остаются неизменными на протяжении всего жизненного цикла данного экземпляра. Любая операция, которая внешне кажется изменением, на самом деле приводит к созданию нового объекта с обновленными свойствами.

Принципиальное отличие неизменяемых типов от изменяемых (мутабельных), таких как Структура, Массив, ТаблицаЗначений, заключается в способе их сравнения. Для изменяемых типов оператор равенства (=) обычно сравнивает ссылки на объекты в памяти. Это значит, что Новый Структура = Новый Структура всегда будет Ложь, потому что мы создаем два разных объекта в памяти, даже если их содержимое изначально идентично. Они имеют разные "адреса", разные ссылки.

Однако, для неизменяемых типов сравнение происходит по значению. Это означает, что платформа 1С сравнивает все свойства двух объектов. Если все свойства (например, для Шрифт это ИмяШрифта, Размер, Жирный, Курсив, Подчеркивание, Зачеркнутый и т.д.) идентичны, то эти два объекта считаются равными, даже если они физически расположены в разных областях памяти или были созданы разными способами.

Поведение типа "Шрифт" и роль кэширования конструктора

Тип Шрифт является ярким представителем неизменяемых типов. Когда мы создаем экземпляр шрифта, например, Новый Шрифт("Arial"), мы задаем его начальные характеристики. Эти характеристики не могут быть изменены в дальнейшем.

Изначальная загадка, описанная в сообщении 0:


Новый Структура = Новый Структура   // дает Ложь, это понятно
Новый Шрифт = Новый Шрифт          // Истина
Новый Шрифт("Arial") = Новый Шрифт("Arial")   // Истина

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

Здесь важно упомянуть механизм кэширования конструкторов, о котором говорится в сообщении 2. Платформа 1С для некоторых неизменяемых типов, включая Шрифт и ОписаниеТипа, может кэшировать результаты вызова конструктора. Это означает, что если мы несколько раз вызовем конструктор с одними и теми же параметрами, платформа может вернуть ссылку на уже существующий, ранее созданный и кэшированный экземпляр объекта. Это оптимизация, позволяющая экономить память и время на создание повторяющихся объектов.

Таким образом, когда мы пишем Новый Шрифт("Arial") = Новый Шрифт("Arial"), вполне возможно, что обе части выражения возвращают ссылку на один и тот же кэшированный объект, что, конечно же, приведет к Истина при сравнении ссылок. Это может создать ложное впечатление, что сравнение идет по ссылке. Однако, критически важно понимать: кэширование – это оптимизация, а фундаментальное правило для неизменяемых типов – это сравнение по значению.

Проверка сравнения по значению при различных способах создания объектов

Возникает логичный вопрос: а что, если объекты не могут быть кэшированы или создаются в разных контекстах (например, на клиенте и на сервере, как упоминается в сообщениях 3 и 5)? Будут ли они все равно считаться равными, если их свойства идентичны? Ответ – да, будут. Это и подтверждает принцип сравнения по значению.

Давайте рассмотрим несколько примеров, которые демонстрируют это поведение, даже когда кэширование конструктора не является определяющим фактором.

Пример 1: Создание объектов разными способами

Предположим, мы хотим создать два объекта Шрифт с одинаковыми параметрами, но используем для этого разные методы создания. Один объект создадим с помощью прямого вызова конструктора, а другой – с помощью универсального метода Новый().


// Создаем первый объект Шрифт через прямой вызов конструктора
ш1 = Новый Шрифт("Arial");

// Создаем параметры для второго объекта
Параметры = Новый Массив;
Параметры.Добавить("Arial");

// Создаем второй объект Шрифт через универсальный метод Новый()
ш2 = Новый("Шрифт", Параметры);

// Сравниваем два объекта
Сообщить("Шрифт 1 = Шрифт 2: " + (ш1 = ш2));

В данном случае, как показано в сообщении 6, результат выражения ш1 = ш2 будет Истина. Это происходит потому, что оба объекта, несмотря на различные способы их создания, имеют идентичные значения свойств (в данном случае, имя шрифта "Arial" и все остальные свойства по умолчанию). Даже если платформа не вернула одну и ту же ссылку из кэша (что вполне возможно при использовании Новый()), сравнение по значению все равно определит их как равные.

Пример 2: Десериализация объекта из XML

Еще один показательный сценарий – это десериализация объекта Шрифт из XML-строки. Когда мы десериализуем объект, мы фактически создаем новый физический экземпляр в памяти, который не связан с какими-либо ранее кэшированными объектами. Если свойства этого десериализованного объекта совпадают со свойствами другого объекта Шрифт, они будут считаться равными.


// Создаем объект Шрифт для сравнения, например, из Примера 1
ш2 = Новый Шрифт("Arial"); // Или можно использовать ш2 из предыдущего примера

// XML-строка, представляющая объект Шрифт
// Обратите внимание на полное описание свойств шрифта
ТекстXML = "<Font xmlns=""http://v8.1c.ru/8.1/data/ui""; xmlns:xs=""http://www.w3.org/2001/XMLSchema""; xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""; xsi:type=""Font"" faceName=""Arial"" height=""0"" bold=""false"" italic=""false"" underline=""false"" strikeout=""false"" kind=""Absolute"" scale=""100""/>";

// Создаем объект для чтения XML
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ТекстXML);

// Десериализуем объект Шрифт из XML
ш3 = СериализаторXDTO.ПрочитатьXML(Чтение);

// Сравниваем десериализованный объект с ранее созданным
Сообщить("Шрифт 2 = Шрифт 3 (из XML): " + (ш2 = ш3));

Как указано в сообщении 7, результат ш2 = ш3 также будет Истина. Объект ш3 был создан путем парсинга XML, что гарантирует создание нового экземпляра. Тем не менее, поскольку все его свойства (faceName="Arial", height="0", bold="false" и т.д.) совпадают с соответствующими свойствами ш2, они считаются равными. Этот пример окончательно подтверждает, что для неизменяемых типов сравнение всегда идет по значению, независимо от способа их создания и их физического расположения в памяти.

Важные выводы и практическое применение

  1. Неизменяемые типы сравниваются по значению: Это главный принцип. Если все свойства двух объектов неизменяемого типа идентичны, они будут считаться равными оператором =.
  2. Шрифт является неизменяемым типом: Свойства объекта Шрифт (имя, размер, стиль) нельзя изменить после создания. Любые "изменения" создают новый экземпляр.
  3. Кэширование конструкторов – это оптимизация: Платформа 1С может возвращать одну и ту же ссылку на кэшированный объект при многократном вызове конструктора с одинаковыми параметрами. Это может сбить с толку, но не отменяет правила сравнения по значению.
  4. Сравнение по значению работает всегда: Даже если объекты созданы разными методами (прямой конструктор, Новый(), десериализация) или в разных контекстах, и не являются одним и тем же кэшированным экземпляром, они будут считаться равными, если их свойства совпадают.
  5. Отличие от изменяемых типов: Всегда помните, что изменяемые типы (Структура, Массив) сравниваются по ссылке, а не по значению. Это фундаментальное различие, которое влияет на логику работы с данными.

Понимание этого принципа крайне важно для написания корректного и предсказуемого кода в 1С. Когда мы работаем с такими типами, как Шрифт, ОписаниеТипа, Дата, мы можем быть уверены, что сравнение объектов с идентичными характеристиками всегда даст Истина, независимо от истории их создания.

← На главную