Почему метод Найти() в табличной части 1С возвращает первую строку при поиске нуля по колонке НомерСтроки

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

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

В этой статье мы подробно разберем внутренние механизмы платформы «1С:Предприятие», выясним техническую причину этой особенности, которая тянется еще со старых релизов платформы 1С 8.2, и рассмотрим наиболее быстрые и безопасные альтернативные методы получения строки по её номеру.

Анализируем проблемную ситуацию на примере кода

Рассмотрим классический пример кода, который часто пишут начинающие программисты, пытаясь перебрать или найти строку по её порядковому номеру:


Для Сч = -1 По 3 Цикл
    Строка = ТабличнаяЧастьДокумента.Найти(Сч, "НомерСтроки");
    Если Строка = Неопределено Тогда
        Сообщить("При Сч = " + Сч + ", строка не найдена");
    Иначе
        Сообщить("При Сч = " + Сч + ", найдена строка " + Строка.НомерСтроки);
    КонецЕсли;
КонецЦикла;

При выполнении этого цикла для значений -1, 1, 2 и 3 система отрабатывает штатно согласно синтакс-помощнику. Но как только переменная Сч принимает значение 0, метод возвращает ссылку на первую строку табличной части, хотя строки с номером 0 в табличной части физически существовать не может — нумерация в интерфейсе 1С всегда начинается с единицы!

Для того чтобы понять, почему это происходит, нам потребуется заглянуть «под капот» работы с коллекциями в платформе 1С.

1. Почему «НомерСтроки» — это виртуальное поле

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

Значение реквизита НомерСтроки жестко привязано к реальному физическому индексу строки в памяти по формуле: НомерСтроки = Индекс + 1.

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

2. Как метод Найти() обрабатывает несуществующие колонки

Поведение платформы при передаче неверного имени колонки в методах поиска различается:

Когда имя колонки игнорируется, метод Найти() автоматически переходит в режим полнотекстового поиска значения по всем доступным колонкам табличной части.

3. Ловушка значения «0» при поиске по всем колонкам

Теперь по шагам разберем, что происходит в оперативной памяти компьютера при выполнении строки кода ТабличнаяЧастьДокумента.Найти(0, "НомерСтроки"):

  1. Платформа определяет, что физической колонки "НомерСтроки" в табличной части нет.
  2. Второй параметр метода отбрасывается, запускается глобальный поиск числа 0 по всем колонкам таблицы.
  3. В платформе 1С все незаполненные числовые реквизиты (такие как Количество, Сумма, Скидка, Коэффициент) по умолчанию инициализируются нулевым значением (0).
  4. Платформа начинает сканировать строки табличной части сверху вниз. В первой же строке, где хотя бы один числовой реквизит не заполнен или равен 0, метод фиксирует совпадение и мгновенно возвращает эту строку разработчику.

Провести эксперимент и подтвердить это поведение очень просто. Если вы полностью заполните все числовые поля первой строки ненулевыми значениями (например, укажете количество 1, цену 100, скидку 5), то метод Найти() при поиске нуля вернет уже вторую строку табличной части. Если заполнить ненулевыми значениями абсолютно все числовые реквизиты во всех строках таблицы, метод наконец-то вернет значение Неопределено.

4. В чем разница между Табличной Частью и Таблицей Значений

Важно провести четкую границу между поведением объектов ТабличнаяЧасть и ТаблицаЗначений:

Правильные альтернативы для разработчика

Поскольку использовать метод Найти() для поиска строки по её номеру в табличной части некорректно, рассмотрим правильные и безопасные способы решения этой задачи.

Способ 1. Прямое обращение по индексу (самый быстрый и правильный метод)

Поскольку физический индекс строки в коллекции всегда равен НомерСтроки - 1, самым эффективным способом является обращение к строке по её индексу в квадратных скобках. Чтобы избежать фатальной ошибки «Индекс находится за границами диапазона» при обращении к несуществующей строке, предварительно выполним проверку общего количества строк в коллекции:


ИскомыйНомер = 2; // Номер строки, которую мы хотим получить

Если ИскомыйНомер > 0 И ИскомыйНомер <= ТабличнаяЧастьДокумента.Количество() Тогда
    Строка = ТабличнаяЧастьДокумента[ИскомыйНомер - 1];
    Сообщить("Успешно получена строка: " + Строка.НомерСтроки);
Иначе
    Строка = Неопределено;
    Сообщить("Строка с таким номером отсутствует в табличной части!");
КонецЕсли;

Способ 2. Использование метода Получить()

Метод Получить() также осуществляет доступ к элементу коллекции по его индексу (начиная с 0). Этот способ аналогичен первому, но требует явного вызова метода. Проверка границ коллекции здесь также строго обязательна:


ИндексСтроки = ИскомыйНомер - 1;

Если ИндексСтроки >= 0 И ИндексСтроки < ТабличнаяЧастьДокумента.Количество() Тогда
    Строка = ТабличнаяЧастьДокумента.Получить(ИндексСтроки);
Иначе
    Строка = Неопределено;
КонецЕсли;

Используя прямое обращение по индексу вместо метода Найти(), мы не только избавляемся от скрытых багов платформы с поиском нулевых значений, но и значительно увеличиваем скорость работы алгоритма. Прямой доступ к элементу в памяти по его индексу происходит мгновенно (за константное время O(1)), в то время как метод Найти() последовательно перебирает все строки и колонки таблицы (за время O(N*M)), неоправданно расходуя ресурсы процессора.

← На главную