В процессе разработки на платформе 1С:Предприятие 8.3 многие программисты, особенно переходящие с обычных форм на управляемые, сталкиваются с неочевидным поведением таблиц. Одной из самых распространенных проблем является некорректная обработка выделенных строк, когда вместо ожидаемого индекса система возвращает значение, которое постоянно увеличивается при каждом перезаполнении таблицы. В этой статье мы подробно разберем, почему так происходит, в чем принципиальное различие между индексом и идентификатором, и как написать код, который будет работать стабильно в любой ситуации.
Рассмотрим ситуацию, которую описал автор темы. Имеется табличная часть на форме, и программист хочет выполнить некое действие для всех строк, которые выделил пользователь. Стандартный подход, который кажется логичным на первый взгляд, выглядит так:
&НаСервере
Процедура ПолучитьРезультатыНаСервере()
ТЧ = Объект.Запросы;
ВыделенныеСтроки = Элементы.СписокЗапросов.ВыделенныеСтроки;
Для Каждого ТекСтрока Из ВыделенныеСтроки Цикл
// Ошибка здесь: ТекСтрока используется как индекс массива ТЧ
ПолучениеРезультата(ТЧ[ТекСтрока]);
КонецЦикла;
ОбновитьСписокЗаявок();
КонецПроцедуры
Проблема этого кода заключается в том, что в управляемом приложении свойство ВыделенныеСтроки возвращает массив идентификаторов строк, а не их индексов. Если в первый раз при открытии формы идентификатор может совпадать с индексом (0, 1, 2...), то после любого программного изменения состава таблицы (очистка, добавление новых данных, метод Загрузить()) идентификаторы начинают расти. Платформа 1С использует внутренний инкрементный счетчик для каждой новой строки в рамках сеанса работы формы. Таким образом, если вы удалили 10 строк и добавили 10 новых, их идентификаторы начнут отсчет с 11, в то время как индексы снова будут от 0 до 9.
Чтобы раз и навсегда разобраться в вопросе, проанализируем определения этих терминов в контексте 1С:
Количество() - 1. Индексы меняются при сортировке, удалении вышестоящих строк или вставке новых элементов в начало списка.Посмотрим на ситуацию на практике: обращение ТЧ[ТекСтрока] работает только тогда, когда ТекСтрока является числом-индексом. Как только идентификатор становится больше, чем количество строк в таблице, программа выдает ошибку "Индекс находится вне границ диапазона" или обращается к совершенно другой строке.
Выясним, как правильно обращаться к данным, имея на руках массив идентификаторов. Для всех коллекций данных формы (таких как ДанныеФормыКоллекция) предусмотрен специальный высокооптимизированный метод НайтиПоИдентификатору(). Рассмотрим исправленный и безопасный вариант кода:
&НаСервере
Процедура ПолучитьРезультатыНаСервере()
// Получаем коллекцию данных табличной части
ТЧ = Объект.Запросы;
// Получаем массив идентификаторов выделенных пользователем строк
ВыделенныеСтроки = Элементы.СписокЗапросов.ВыделенныеСтроки;
Для Каждого ИдентификаторСтроки Из ВыделенныеСтроки Цикл
// Правильное получение объекта строки по её ID
СтрокаТЧ = ТЧ.НайтиПоИдентификатору(ИдентификаторСтроки);
// Обязательно проверяем, что строка найдена (она могла быть удалена другим процессом)
Если СтрокаТЧ <> Неопределено Тогда
ПолучениеРезультата(СтрокаТЧ);
КонецЕсли;
КонецЦикла;
ОбновитьСписокЗаявок();
КонецПроцедуры
Рассмотрим подробнее, почему этот метод лучше. Во-первых, он работает корректно даже после сортировки или программного изменения состава таблицы. Во-вторых, он избавляет разработчика от необходимости знать текущий индекс строки. Платформа сама сопоставляет внутренний ID с объектом данных.
Важно помнить, что поведение свойства ВыделенныеСтроки кардинально меняется, если ваша таблица на форме связана не с табличной частью документа, а с Динамическим списком. Проанализируем эти различия:
Если ваша таблица — это динамический список, то использовать НайтиПоИдентификатору не нужно. В цикле вы сразу получите ссылку на документ или элемент справочника, которую можно передавать в процедуры обработки данных.
Многие задаются вопросом: почему при вызове метода Очистить() счетчик идентификаторов не сбрасывается в ноль? Это архитектурная особенность платформы. Идентификатор должен быть уникальным в рамках "жизни" формы. Если бы система сбрасывала счетчик, могла бы возникнуть ситуация, когда старый идентификатор, который еще "помнит" клиентская часть или какой-то асинхронный процесс, внезапно стал указывать на новую, совершенно другую строку. Чтобы избежать такой путаницы и ошибок синхронизации между клиентом и сервером, платформа просто наращивает счетчик до тех пор, пока форма не будет закрыта.
Разберем по шагам, как гарантировать стабильность работы вашего кода при манипуляциях с таблицами:
НайтиПоИдентификатору() при работе с ДанныеФормыКоллекция, если вы получили данные из свойств ВыделенныеСтроки или ТекущаяСтрока.Загрузить()), помните, что все старые идентификаторы становятся недействительными. Если пользователь до этого момента что-то выделил, его выделение либо сбросится, либо станет некорректным. В таких случаях хорошим тоном считается принудительная очистка выделения через Элементы.ИмяТаблицы.ВыделенныеСтроки.Очистить() или программная установка выделения на новые строки.Неопределено после поиска по ID. В многопользовательской среде или при сложных алгоритмах строка может быть удалена в промежутке между действием пользователя и выполнением серверного кода.Соблюдение этих простых правил позволит вам избежать трудноуловимых ошибок (багов), которые проявляются "через раз" и зависят от того, сколько раз пользователь нажал кнопку обновления данных.