В процессе разработки на платформе «1С:Предприятие 8» программисты часто сталкиваются с необходимостью быстрого поиска данных в таблицах. Казалось бы, методы Найти() и НайтиСтроки() интуитивно понятны, однако при использовании системного поля НомерСтроки поведение системы может стать непредсказуемым для неофита. В этой статье мы подробно разберем, почему один и тот же код выдает разные результаты в зависимости от контекста и типа объекта, а также рассмотрим критические изменения в поведении платформы начиная с версии 8.3.24.
Представим ситуацию: у нас есть документ «Заказ клиента» с табличной частью Товары. В этой таблице содержатся четыре строки:
Рассмотрим код, который выполняет различные варианты поиска, и проанализируем результаты каждого шага.
об = Объект.СсылкаНаДокумент.ПолучитьОбъект();
// Вариант 1: Поиск методом Найти
МояСтрока = об.Товары.Найти(1, "НомерСтроки");
// Вариант 2: Поиск методом НайтиСтроки в ТЧ по номеру
Мас1 = об.Товары.НайтиСтроки(New Структура("НомерСтроки", 1));
// Вариант 3: Поиск методом НайтиСтроки по номеру и номенклатуре
Мас3 = об.Товары.НайтиСтроки(New Структура("НомерСтроки, Номенклатура", 1, об.Товары[0].Номенклатура));
// Вариант 4: Поиск в выгруженной Таблице Значений
Мас4 = об.Товары.Выгрузить().НайтиСтроки(New Структура("НомерСтроки, Номенклатура", 1, об.Товары[0].Номенклатура));
Начнем с классического метода Найти(). При выполнении кода об.Товары.Найти(1, "НомерСтроки") переменная МояСтрока будет содержать первую строку табличной части. Несмотря на то, что НомерСтроки является системным полем, метод Найти() умеет работать с ним напрямую.
Важный нюанс: До версии платформы 8.3.24 у этого метода существовала особенность (баг, ставший фичей). Если вы указывали колонку поиска НомерСтроки, но искомое значение не находилось (например, вы искали строку номер 10, а их всего 5), 1С не возвращала Неопределено сразу. Вместо этого платформа начинала «жадный» поиск по всем остальным колонкам. Если в какой-то строке в колонке «Количество» или «Сумма» встречалось число 10, метод возвращал эту строку. В современных версиях (8.3.24+) это поведение исправлено: поиск ограничен строго указанной колонкой.
Рассмотрим вызов об.Товары.НайтиСтроки(New Структура("НомерСтроки", 1)). Здесь многих ждет сюрприз: Мас1.Количество() будет равно 0.
Почему так происходит? Дело в том, что объект ТабличнаяЧасть воспринимает НомерСтроки как вычисляемое системное свойство, а не как полноценный реквизит данных. Метод НайтиСтроки() спроектирован для работы с физическими колонками данных. Поскольку в метаданных табличной части НомерСтроки не является хранимым реквизитом (он генерируется динамически), метод просто «не видит» эту колонку в структуре поиска для объекта ТЧ.
Теперь проанализируем ситуацию с поиском по двум полям: об.Товары.НайтиСтроки(New Структура("НомерСтроки, Номенклатура", 1, "Товар1")). Логично было бы ожидать, что мы получим одну строку (первую), так как только она соответствует обоим условиям.
Однако результат Мас3.Количество() будет равен 2. Проанализируем причину этого явления:
НомерСтроки и Номенклатура.ТабличнаяЧасть, 1С отбрасывает ключ НомерСтроки, так как считает его недопустимым для этого метода.Это крайне опасная ситуация, так как она не вызывает исключения (ошибки), но возвращает неверные с точки зрения бизнес-логики данные. Программист может ожидать уникальный результат, а получить массив «дублей».
Последний пример: об.Товары.Выгрузить().НайтиСтроки(...). Здесь Мас4.Количество() будет равно 1. Это правильный результат.
Выясним, что изменилось. Метод Выгрузить() создает объект типа ТаблицаЗначений. В момент выгрузки системное поле НомерСтроки превращается в обычную колонку типа Число. Теперь это полноценные данные, по которым метод НайтиСтроки() может выполнять фильтрацию. В этом контексте условие по номеру строки отрабатывает корректно вместе с условием по номенклатуре.
Проанализировав все варианты, мы можем сделать важные выводы для практической работы:
ТабличнойЧасти (объекта документа/справочника) и для ТаблицыЗначений (универсальной коллекции в памяти).Найти() или обращайтесь напрямую по индексу: об.Товары[НужныйНомер - 1].ТаблицуЗначений. Это сделает поиск предсказуемым.ТаблицуЗначений (ТЗ.Индексы.Добавить("Номенклатура")) значительно ускорит работу НайтиСтроки(). В объекте ТабличнаяЧасть создать индекс программно невозможно.В контексте управляемых форм для работы с конкретными строками рекомендуется использовать ИдентификаторСтроки, а не её номер. Номер строки может измениться при сортировке или удалении других строк, тогда как идентификатор остается неизменным в течение сеанса работы с формой. Это позволяет избежать ошибок, когда код обращается к «строке №5», которая после удаления «строки №2» стала «строкой №4».
Рассмотрим пример получения строки по идентификатору в контексте формы:
// Получаем идентификатор текущей строки
Идентификатор = Элементы.Товары.ТекущаяСтрока;
// Находим данные этой строки в коллекции
СтрокаДанных = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
Этот метод является наиболее надежным и «православным» с точки зрения архитектуры 1С:Предприятия при работе в интерфейсе. При необходимости реализовать фильтрацию подчиненных данных по текущей строке можно использовать механизм связывания табличных частей на управляемых формах.
Мы рассмотрели тонкости работы с коллекциями строк и выяснили, что внимательность к типам объектов — залог отсутствия трудноуловимых багов. Помните, что НомерСтроки в 1С — это «особая» колонка, которая ведет себя как данные только тогда, когда вы явно превращаете её в данные (через выгрузку в ТЗ). В остальное время — это лишь порядковый указатель, требующий специфических методов обращения.