Почему условие не выполняется при обходе СпискаЗначений в 1С 8.3?

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

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

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

Суть проблемы: код есть, условия нет

Рассмотрим конкретный пример кода, который вызывает вопросы. Представим, что у нас есть задача создать документы «Ввод начальных остатков» для разных видов операций (расчеты с покупателями и поставщиками). Код выглядит логично и структурировано:


СписокВидовОпераций = Новый СписокЗначений;
СписокВидовОпераций.Добавить(Перечисления.ВидыОпераций.РасчетыСПокупателями);
СписокВидовОпераций.Добавить(Перечисления.ВидыОпераций.РасчетыСПоставщиками);

Для Каждого ВидОперации Из СписокВидовОпераций Цикл
    ДокВвода = Документы.ВводНачальныхОстатков.СоздатьДокумент();
    ДокВвода.ВидОперации = ВидОперации; 
    
    // Проблемное место: условие ниже не выполняется
    Если ДокВвода.ВидОперации = Перечисления.ВидыОпераций.РасчетыСПокупателями Тогда
        // Какой-то код
    КонецЕсли;
КонецЦикла;

На первый взгляд все верно: мы добавляем перечисления в список, затем перебираем их и сравниваем. Однако при отладке выясняется, что даже когда очередь доходит до «Расчетов с покупателями», программа не заходит внутрь условия Если. Почему так происходит? Давайте разбираться детально.

Анализ типов данных: что мы перебираем?

Главная ошибка кроется в понимании того, что именно попадает в переменную итератора (в нашем случае переменную ВидОперации) при обходе коллекции СписокЗначений.

Платформа 1С:Предприятие имеет строгую типизацию объектов. Давайте посмотрим, чем отличаются коллекции Массив и СписокЗначений.

  1. Массив (Array): Это простая коллекция, хранящая сами значения. Если вы положите в массив Ссылку, то при обходе массива вы получите именно Ссылку.
  2. СписокЗначений (ValueList): Это объект, предназначенный в первую очередь для работы с пользовательским интерфейсом (для выбора из списка, отображения на форме).

Когда мы выполняем метод СписокВидовОпераций.Добавить(Значение), платформа создает специальный объект-обертку — ЭлементСпискаЗначений. Этот объект содержит несколько свойств:

Теперь проанализируем наш цикл:


Для Каждого ВидОперации Из СписокВидовОпераций Цикл
    // В этот момент переменная ВидОперации имеет тип "ЭлементСпискаЗначений"
    // А НЕ "ПеречислениеСсылка"
    
    // Здесь происходит неявное присваивание, или запись объекта-обертки, 
    // что может привести к ошибке типов или некорректному заполнению
    ДокВвода.ВидОперации = ВидОперации; 
КонецЦикла;

Когда выполняется строка Если ДокВвода.ВидОперации = Перечисления..., происходит сравнение двух разных сущностей:

  1. Слева: Ссылка на перечисление (если присваивание прошло успешно благодаря извлечению значения платформой, либо там остался ЭлементСпискаЗначений, если реквизит составного типа или типа Произвольный).
  2. Справа: Ссылка на перечисление.
  3. В переменной цикла ВидОперации: Объект ЭлементСпискаЗначений.

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


// Это сравнение ВСЕГДА будет Ложь
Если ВидОперации = Перечисления.ВидыОпераций.РасчетыСПокупателями Тогда

Оно ложно, потому что Элемент списка значений (обертка) не равен Значению (содержимому), которое лежит внутри него.

Решение проблемы: 2 способа

Разберем два правильных способа исправить этот код.

Способ 1: Использование свойства "Значение"

Если вам необходимо использовать именно СписокЗначений (например, он приходит с формы или используется для отбора), то при обращении к элементу цикла нужно явно указывать свойство .Значение.


Для Каждого ЭлементСписка Из СписокВидовОпераций Цикл
    
    // Получаем реальное значение перечисления из обертки
    РеальныйВидОперации = ЭлементСписка.Значение;
    
    ДокВвода = Документы.ВводНачальныхОстатков.СоздатьДокумент();
    ДокВвода.ВидОперации = РеальныйВидОперации;
    
    // Теперь сравниваем Ссылку со Ссылкой - условие сработает
    Если РеальныйВидОперации = Перечисления.ВидыОпераций.РасчетыСПокупателями Тогда
        // Код выполняется
    КонецЕсли;

КонецЦикла;

Способ 2: Использование Массива (Рекомендуемый)

Если коллекция создается исключительно программно для алгоритма и не показывается пользователю в интерфейсе, использование СпискаЗначений избыточно. Он потребляет больше ресурсов памяти из-за создания объектов-оберток. Правильнее использовать Массив.


// Используем Массив вместо СпискаЗначений
МассивВидов = Новый Массив;
МассивВидов.Добавить(Перечисления.ВидыОпераций.РасчетыСПокупателями);
МассивВидов.Добавить(Перечисления.ВидыОпераций.РасчетыСПоставщиками);

Для Каждого ВидОперации Из МассивВидов Цикл
    // При итерации массива переменная сразу содержит ЗНАЧЕНИЕ (ПеречислениеСсылка)
    
    ДокВвода = Документы.ВводНачальныхОстатков.СоздатьДокумент();
    ДокВвода.ВидОперации = ВидОперации;
    
    // Условие работает корректно без дополнительных свойств
    Если ВидОперации = Перечисления.ВидыОпераций.РасчетыСПокупателями Тогда
        // Код выполняется
    КонецЕсли;
КонецЦикла;

Миф о работе в версии 8.2

Часто можно услышать: "Но в 8.2 это работало!". Давайте проанализируем это утверждение. Объектная модель СпискаЗначений не менялась кардинально между версиями 8.2 и 8.3 в плане структуры элемента. Скорее всего, имеют место следующие факторы:

  1. В старом коде использовался Массив, а при переписывании случайно заменили на СписокЗначений.
  2. Использовался другой способ обхода (например, через индекс Получить(i)), где могло происходить обращение к значению.
  3. В некоторых контекстах (толстый клиент) неявное приведение типов могло работать иначе, либо сравнивались свойства, которые случайно совпадали (например, строковые представления, хотя это плохая практика).

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

Вторая проблема: Цикл по Таблице Значений игнорируется

Рассмотрим еще один вопрос, возникший в ходе обсуждения: почему отладчик игнорирует цикл Для Каждого Стр Из ТаблицаЗначений? Курсор отладчика просто "перепрыгивает" тело цикла, не заходя внутрь.

Существует три основные причины такого поведения. Проанализируем их от простого к сложному.

1. Пустая коллекция

Самая банальная и частая причина — в таблице значений просто нет строк. Визуально или логически вам может казаться, что данные там должны быть, но на момент выполнения цикла таблица пуста.

Как проверить:

Перед циклом поставьте точку останова и используйте "Вычислить выражение" (Shift+F9) для команды:

Результат.Количество()

Если вернется 0, значит, проблема в заполнении таблицы или запросе, а не в цикле.

2. Результат запроса vs Таблица значений

Иногда переменная называется Таблица, но по факту содержит тип РезультатЗапроса. У этого объекта другая логика обхода.

Если вы используете конструкцию Для Каждого для результата запроса, она сработает, но это не самый оптимальный путь (лучше использовать Выборка = Результат.Выбрать(); Пока Выборка.Следующий()...). Однако, если результат запроса был предварительно выгружен (метод Выгрузить()), то мы работаем с таблицей.

3. Контекст Клиент-Сервер (Особенность 8.3)

Это критически важный момент для управляемого приложения (версия 8.3). Тип ТаблицаЗначений доступен только на сервере.

Если код выполняется на клиенте, и вы пытаетесь работать с объектом, который там недоступен или пуст из-за особенностей контекста, цикл может не выполниться.

Рекомендации по отладке

Чтобы в будущем избегать подобных ситуаций и не гадать "почему код не работает", выработайте привычку проверять типы данных прямо в отладчике.

  1. Поставьте точку останова (F9) на строке с началом цикла или условия.
  2. Когда выполнение остановится, выделите переменную (например, ВидОперации).
  3. Нажмите Shift+F9 (Вычислить выражение).
  4. Посмотрите не только на значение, но и на колонку Тип.

Если вы увидите тип ЭлементСпискаЗначений, а ожидаете ПеречислениеСсылка, вы сразу поймете, что нужно обратиться к свойству .Значение.

Также полезна функция ТипЗнч(Переменная). Вы можете написать в табло отладчика:

ТипЗнч(ВидОперации)

Это вернет точный тип объекта, с которым вы работаете в данный момент.

Заключение

Работа с коллекциями в 1С требует внимания к деталям реализации объектов. СписокЗначений — это "обертка" для удобства интерфейса, а Массив — контейнер для данных. Понимание этой разницы и использование отладчика для проверки типов гарантирует, что ваши условия и циклы будут работать корректно на любой версии платформы.

← На главную